diff --git a/applets/kicker/plugin/dashboardwindow.cpp b/applets/kicker/plugin/dashboardwindow.cpp index ff8bc5d73..89f4c11d4 100644 --- a/applets/kicker/plugin/dashboardwindow.cpp +++ b/applets/kicker/plugin/dashboardwindow.cpp @@ -1,208 +1,218 @@ /*************************************************************************** * Copyright (C) 2015 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 . * ***************************************************************************/ #include "dashboardwindow.h" #include #include #include #include #include DashboardWindow::DashboardWindow(QQuickItem *parent) : QQuickWindow(parent ? parent->window() : 0) , m_mainItem(0) , m_visualParentItem(0) , m_visualParentWindow(0) { setClearBeforeRendering(true); setColor(QColor(0, 0, 0, 188)); setFlags(Qt::FramelessWindowHint); setIcon(QIcon::fromTheme("plasma")); connect(&m_theme, &Plasma::Theme::themeChanged, this, &DashboardWindow::updateTheme); } DashboardWindow::~DashboardWindow() { } QQuickItem *DashboardWindow::mainItem() const { return m_mainItem; } void DashboardWindow::setMainItem(QQuickItem *item) { if (m_mainItem != item) { if (m_mainItem) { m_mainItem->setVisible(false); } m_mainItem = item; if (m_mainItem) { m_mainItem->setVisible(isVisible()); m_mainItem->setParentItem(contentItem()); } emit mainItemChanged(); } } QQuickItem *DashboardWindow::visualParent() const { return m_visualParentItem; } void DashboardWindow::setVisualParent(QQuickItem *item) { if (m_visualParentItem != item) { if (m_visualParentItem) { disconnect(m_visualParentItem.data(), &QQuickItem::windowChanged, this, &DashboardWindow::visualParentWindowChanged); } m_visualParentItem = item; if (m_visualParentItem) { if (m_visualParentItem->window()) { visualParentWindowChanged(m_visualParentItem->window()); } connect(m_visualParentItem.data(), &QQuickItem::windowChanged, this, &DashboardWindow::visualParentWindowChanged); } emit visualParentChanged(); } } QQuickItem *DashboardWindow::keyEventProxy() const { return m_keyEventProxy; } void DashboardWindow::setKeyEventProxy(QQuickItem *item) { if (m_keyEventProxy != item) { m_keyEventProxy = item; emit keyEventProxyChanged(); } } void DashboardWindow::toggle() { if (isVisible()) { close(); } else { resize(screen()->size()); showFullScreen(); KWindowSystem::forceActiveWindow(winId()); } } bool DashboardWindow::event(QEvent *event) { if (event->type() == QEvent::Expose) { + // FIXME TODO: We can remove this once we depend on Qt 5.6.1+. + // See: https://bugreports.qt.io/browse/QTBUG-26978 KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); +#if (QT_VERSION > QT_VERSION_CHECK(5, 5, 0)) + } else if (event->type() == QEvent::PlatformSurface) { + const QPlatformSurfaceEvent *pSEvent = static_cast(event); + + if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { + KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); + } +#endif } else if (event->type() == QEvent::Show) { updateTheme(); if (m_mainItem) { m_mainItem->setVisible(true); } } else if (event->type() == QEvent::Hide) { if (m_mainItem) { m_mainItem->setVisible(false); } } else if (event->type() == QEvent::FocusOut) { if (isVisible()) { KWindowSystem::raiseWindow(winId()); KWindowSystem::forceActiveWindow(winId()); } } return QQuickWindow::event(event); } void DashboardWindow::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { emit keyEscapePressed(); return; } else if (m_keyEventProxy && !m_keyEventProxy->hasActiveFocus() && !(e->key() == Qt::Key_Home) && !(e->key() == Qt::Key_End) && !(e->key() == Qt::Key_Left) && !(e->key() == Qt::Key_Up) && !(e->key() == Qt::Key_Right) && !(e->key() == Qt::Key_Down) && !(e->key() == Qt::Key_PageUp) && !(e->key() == Qt::Key_PageDown) && !(e->key() == Qt::Key_Enter) && !(e->key() == Qt::Key_Return) && !(e->key() == Qt::Key_Menu)) { QPointer previousFocusItem = activeFocusItem(); m_keyEventProxy->forceActiveFocus(); QEvent* eventCopy = new QKeyEvent(e->type(), e->key(), e->modifiers(), e->nativeScanCode(), e->nativeVirtualKey(), e->nativeModifiers(), e->text(), e->isAutoRepeat(), e->count()); QCoreApplication::postEvent(this, eventCopy); QCoreApplication::processEvents(); if (previousFocusItem) { previousFocusItem->forceActiveFocus(); } return; } QQuickWindow::keyPressEvent(e); } void DashboardWindow::updateTheme() { KWindowEffects::enableBlurBehind(winId(), true); } void DashboardWindow::visualParentWindowChanged(QQuickWindow *window) { if (m_visualParentWindow) { disconnect(m_visualParentWindow.data(), &QQuickWindow::screenChanged, this, &DashboardWindow::visualParentScreenChanged); } m_visualParentWindow = window; if (m_visualParentWindow) { visualParentScreenChanged(m_visualParentWindow->screen()); connect(m_visualParentWindow.data(), &QQuickWindow::screenChanged, this, &DashboardWindow::visualParentScreenChanged); } } void DashboardWindow::visualParentScreenChanged(QScreen *screen) { if (screen) { setScreen(screen); setGeometry(screen->geometry()); } } diff --git a/applets/pager/package/contents/ui/main.qml b/applets/pager/package/contents/ui/main.qml index f63804765..1f9224409 100644 --- a/applets/pager/package/contents/ui/main.qml +++ b/applets/pager/package/contents/ui/main.qml @@ -1,486 +1,486 @@ /* * Copyright 2012 Luís Gabriel Lima * Copyright 2016 Kai Uwe Broulik * Copyright 2016 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, see . */ 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.plasma.components 2.0 as PlasmaComponents import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddonsComponents import org.kde.draganddrop 2.0 import org.kde.plasma.private.pager 2.0 import "utils.js" as Utils MouseArea { id: root property bool isActivityPager: (plasmoid.pluginName == "org.kde.plasma.activitypager") property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) property var activityDataSource: null Layout.minimumWidth: !root.vertical ? pagerItemGrid.width : 1 Layout.minimumHeight: root.vertical ? pagerItemGrid.height : 1 Layout.maximumWidth: !root.vertical ? pagerItemGrid.width : Infinity Layout.maximumHeight: root.vertical ? pagerItemGrid.height : Infinity Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation - Plasmoid.status: pagerModel.count > 1 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.HiddenStatus + Plasmoid.status: pagerModel.shouldShowPager ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.HiddenStatus Layout.fillWidth: root.vertical Layout.fillHeight: !root.vertical property bool dragging: false property int dragId property int dragSwitchDesktopId: -1 anchors.fill: parent acceptedButtons: Qt.NoButton function colorWithAlpha(color, alpha) { return Qt.rgba(color.r, color.g, color.b, alpha) } readonly property color windowActiveOnActiveDesktopColor: colorWithAlpha(theme.textColor, 0.6) readonly property color windowInactiveOnActiveDesktopColor: colorWithAlpha(theme.textColor, 0.35) readonly property color windowActiveColor: colorWithAlpha(theme.textColor, 0.5) readonly property color windowActiveBorderColor: theme.textColor readonly property color windowInactiveColor: colorWithAlpha(theme.textColor, 0.17) readonly property color windowInactiveBorderColor: colorWithAlpha(theme.textColor, 0.5) function action_addDesktop() { pagerModel.addDesktop(); } function action_removeDesktop() { pagerModel.removeDesktop(); } function action_openKCM() { KQuickControlsAddonsComponents.KCMShell.open("desktop"); } function action_showActivityManager() { if (!activityDataSource) { activityDataSource = Qt.createQmlObject('import org.kde.plasma.core 2.0 as PlasmaCore; \ PlasmaCore.DataSource { id: dataSource; engine: "org.kde.activities"; \ connectedSources: ["Status"] }', root); } var service = activityDataSource.serviceForSource("Status") var operation = service.operationDescription("toggleActivityManager") service.startOperationCall(operation) } onWheel: { if (wheel.angleDelta.y > 0 || wheel.angleDelta.x > 0) { pagerModel.changePage((repeater.count + pagerModel.currentPage - 2) % repeater.count); } else { pagerModel.changePage(pagerModel.currentPage % repeater.count); } } PagerModel { id: pagerModel enabled: root.visible showDesktop: (plasmoid.configuration.currentDesktopSelected == 1) showOnlyCurrentScreen: plasmoid.configuration.showOnlyCurrentScreen screenGeometry: plasmoid.screenGeometry pagerType: isActivityPager ? PagerModel.Activities : PagerModel.VirtualDesktops } Connections { target: plasmoid.configuration onShowWindowIconsChanged: { // Causes the model to reset; Component.onCompleted in the // window delegate now gets a chance to create the icon item, // which it otherwise will not do. pagerModel.refresh(); } onDisplayedTextChanged: { // Causes the model to reset; Component.onCompleted in the // desktop delegate now gets a chance to create the label item, // which it otherwise will not do. pagerModel.refresh(); } } Component { id: desktopLabelComponent PlasmaComponents.Label { anchors { fill: parent topMargin: desktopFrame.margins.top bottomMargin: desktopFrame.margins.bottom leftMargin: desktopFrame.margins.left rightMargin: desktopFrame.margins.right } property int index: 0 property var model: null property Item desktopFrame: null text: plasmoid.configuration.displayedText ? model.display : index + 1 wrapMode: Text.NoWrap elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } Component { id: windowIconComponent PlasmaCore.IconItem { anchors.centerIn: parent height: parent.width / 2 width: parent.height / 2 property var model: null source: model ? model.decoration : undefined usesPlasmaTheme: false animated: false } } Timer { id: dragTimer interval: 1000 onTriggered: { if (dragSwitchDesktopId != -1 && dragSwitchDesktopId !== pagerModel.currentPage - 1) { pagerModel.changePage(dragSwitchDesktopId); } } } Grid { id: pagerItemGrid spacing: units.devicePixelRatio rows: effectiveRows columns: effectiveColumns readonly property int effectiveRows: { if (!pagerModel.count) { return 1; } var columns = Math.floor(pagerModel.count / pagerModel.layoutRows); if (pagerModel.count % pagerModel.layoutRows > 0) { columns += 1; } var rows = Math.floor(pagerModel.count / columns); if (pagerModel.count % columns > 0) { rows += 1; } return rows; } readonly property int effectiveColumns: { if (!pagerModel.count) { return 1; } return Math.ceil(pagerModel.count / effectiveRows); } readonly property real pagerItemSizeRatio: pagerModel.pagerItemSize.width / pagerModel.pagerItemSize.height readonly property real widthScaleFactor: columnWidth / pagerModel.pagerItemSize.width readonly property real heightScaleFactor: rowHeight / pagerModel.pagerItemSize.height states: [ State { name: "vertical" when: vertical PropertyChanges { target: pagerItemGrid innerSpacing: effectiveColumns rowHeight: Math.floor(columnWidth / pagerItemSizeRatio) columnWidth: Math.floor((root.width - innerSpacing) / effectiveColumns) } } ] property int innerSpacing: (effectiveRows - 1) * spacing property int rowHeight: Math.floor((root.height - innerSpacing) / effectiveRows) property int columnWidth: Math.floor(rowHeight * pagerItemSizeRatio) Repeater { id: repeater model: pagerModel PlasmaCore.ToolTipArea { id: desktop property int desktopId: index property bool active: isActivityPager ? (index == pagerModel.currentPage) : (index + 1 == pagerModel.currentPage) mainText: model.display // our ToolTip has maximumLineCount of 8 which doesn't fit but QML doesn't // respect that in RichText so we effectively can put in as much as we like :) // it also gives us more flexibility when it comes to styling the
  • textFormat: Text.RichText function updateSubText() { var generateWindowList = function windowList(windows) { // if we have 5 windows, we would show "4 and another one" with the // hint that there's 1 more taking the same amount of space than just showing it var maximum = windows.length === 5 ? 5 : 4 var text = "
    • " + windows.slice(0, maximum).join("
    • ") + "
    " if (windows.length > maximum) { text += i18np("...and %1 other window", "...and %1 other windows", windows.length - maximum) } return text } var text = "" var visibleWindows = [] var minimizedWindows = [] for (var i = 0, length = windowRectRepeater.count; i < length; ++i) { var window = windowRectRepeater.itemAt(i) if (window) { if (window.minimized) { minimizedWindows.push(window.visibleName) } else { visibleWindows.push(window.visibleName) } } } if (visibleWindows.length) { text += i18np("%1 Window:", "%1 Windows:", visibleWindows.length) + generateWindowList(visibleWindows) } if (visibleWindows.length && minimizedWindows.length) { text += "
    " } if (minimizedWindows.length > 0) { text += i18np("%1 Minimized Window:", "%1 Minimized Windows:", minimizedWindows.length) + generateWindowList(minimizedWindows) } if (text.length) { // Get rid of the spacing
      would cause text = "" + text } subText = text } width: pagerItemGrid.columnWidth height: pagerItemGrid.rowHeight PlasmaCore.FrameSvgItem { id: desktopFrame anchors.fill: parent z: 1 // to make sure that the FrameSvg will be placed on top of the windows imagePath: "widgets/pager" prefix: (desktopMouseArea.enabled && desktopMouseArea.containsMouse) || (root.dragging && root.dragId == desktopId) ? "hover" : (desktop.active ? "active" : "normal") } DropArea { id: droparea anchors.fill: parent preventStealing: true onDragEnter: { root.dragSwitchDesktopId = desktop.desktopId; dragTimer.start(); } onDragLeave: { root.dragSwitchDesktopId = -1; dragTimer.stop(); } onDrop: { pagerModel.drop(event.mimeData, desktop.desktopId); root.dragSwitchDesktopId = -1; dragTimer.stop(); } } MouseArea { id: desktopMouseArea anchors.fill: parent hoverEnabled : true onClicked: pagerModel.changePage(desktopId); } Item { id: clipRect x: Math.round(units.devicePixelRatio) y: Math.round(units.devicePixelRatio) width: desktop.width - 2 * x height: desktop.height - 2 * y clip: true Repeater { id: windowRectRepeater model: TasksModel onCountChanged: desktop.updateSubText() Rectangle { id: windowRect z: model.StackingOrder property rect geometry: model.Geometry property int windowId: model.LegacyWinIdList[0] property string visibleName: model.display property bool minimized: (model.IsMinimized === true) onMinimizedChanged: desktop.updateSubText() /* since we move clipRect with 1, move it back */ x: (geometry.x * pagerItemGrid.widthScaleFactor) - Math.round(units.devicePixelRatio) y: (geometry.y * pagerItemGrid.heightScaleFactor) - Math.round(units.devicePixelRatio) width: geometry.width * pagerItemGrid.widthScaleFactor height: geometry.height * pagerItemGrid.heightScaleFactor visible: model.IsMinimized !== true color: { if (desktop.active) { if (model.IsActive === true) return windowActiveOnActiveDesktopColor; else return windowInactiveOnActiveDesktopColor; } else { if (model.IsActive === true) return windowActiveColor; else return windowInactiveColor; } } border.width: Math.round(units.devicePixelRatio) border.color: (model.IsActive === true) ? windowActiveBorderColor : windowInactiveBorderColor MouseArea { id: windowMouseArea anchors.fill: parent drag.target: windowRect drag.axis: Drag.XandYAxis drag.minimumX: -windowRect.width/2 drag.maximumX: root.width - windowRect.width/2 drag.minimumY: -windowRect.height/2 drag.maximumY: root.height - windowRect.height/2 drag.onActiveChanged: { root.dragging = drag.active; root.dragId = desktop.desktopId; desktopMouseArea.enabled = !drag.active; if (drag.active) { // Reparent to allow drags outside of this desktop. var value = root.mapFromItem(clipRect, windowRect.x, windowRect.y); windowRect.parent = root; windowRect.x = value.x; windowRect.y = value.y } } onReleased: { if (root.dragging) { windowRect.visible = false; var windowCenter = Qt.point(windowRect.x + windowRect.width / 2, windowRect.y + windowRect.height / 2); var pagerItem = pagerItemGrid.childAt(windowCenter.x, windowCenter.y); if (pagerItem) { var relativeTopLeft = root.mapToItem(pagerItem, windowRect.x, windowRect.y); pagerModel.moveWindow(windowRect.windowId, relativeTopLeft.x, relativeTopLeft.y, pagerItem.desktopId, root.dragId, pagerItemGrid.widthScaleFactor, pagerItemGrid.heightScaleFactor); } // Will reset the model, destroying the reparented drag delegate that // is no longer bound to model.Geometry. root.dragging = false; pagerModel.refresh(); } else { // When there is no dragging (just a click), the event is passed // to the desktop MouseArea. desktopMouseArea.clicked(mouse); } } } Component.onCompleted: { if (plasmoid.configuration.showWindowIcons) { windowIconComponent.createObject(windowRect, {"model": model}); } } } } } Component.onCompleted: { if (plasmoid.configuration.displayedText < 2) { desktopLabelComponent.createObject(desktop, {"index": index, "model": model, "desktopFrame": desktopFrame}); } } } } } Component.onCompleted: { if (isActivityPager) { plasmoid.setAction("showActivityManager", i18n("Show Activity Manager..."), "preferences-activities"); } else { if (KQuickControlsAddonsComponents.KCMShell.authorize("desktop.desktop").length > 0) { plasmoid.setAction("addDesktop", i18n("Add Virtual Desktop"), "list-add"); plasmoid.setAction("removeDesktop", i18n("Remove Virtual Desktop"), "list-remove"); plasmoid.action("removeDesktop").enabled = Qt.binding(function() { return repeater.count > 1; }); plasmoid.setAction("openKCM", i18n("Configure Desktops..."), "configure"); } } } } diff --git a/applets/pager/plugin/pagermodel.cpp b/applets/pager/plugin/pagermodel.cpp index 99a888014..8602bd66f 100644 --- a/applets/pager/plugin/pagermodel.cpp +++ b/applets/pager/plugin/pagermodel.cpp @@ -1,593 +1,639 @@ /******************************************************************** Copyright 2007 Daniel Laidig Copyright 2012 Luís Gabriel Lima Copyright 2016 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 . *********************************************************************/ #include "pagermodel.h" #include "windowmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace TaskManager; class PagerModel::Private { public: Private(PagerModel *q); ~Private(); static int instanceCount; + bool componentComplete = false; + PagerType pagerType = VirtualDesktops; bool enabled = false; bool showDesktop = false; bool showOnlyCurrentScreen = false; QRect screenGeometry; WindowTasksModel *tasksModel = nullptr; static ActivityInfo *activityInfo; QMetaObject::Connection activityInfoConn; static VirtualDesktopInfo *virtualDesktopInfo; QMetaObject::Connection virtualDesktopNumberConn; QMetaObject::Connection virtualDesktopNamesConn; QDesktopWidget *desktopWidget = QApplication::desktop(); QList windowModels; #if HAVE_X11 QList cachedStackingOrder = KWindowSystem::stackingOrder(); #endif void refreshDataSource(); private: PagerModel *q; }; int PagerModel::Private::instanceCount = 0; ActivityInfo *PagerModel::Private::activityInfo = nullptr; VirtualDesktopInfo *PagerModel::Private::virtualDesktopInfo = nullptr; PagerModel::Private::Private(PagerModel *q) : q(q) { ++instanceCount; if (!activityInfo) { activityInfo = new ActivityInfo(); } + QObject::connect(activityInfo, &ActivityInfo::numberOfRunningActivitiesChanged, + q, &PagerModel::shouldShowPagerChanged); + if (!virtualDesktopInfo) { virtualDesktopInfo = new VirtualDesktopInfo(); } + QObject::connect(virtualDesktopInfo, &VirtualDesktopInfo::numberOfDesktopsChanged, + q, &PagerModel::shouldShowPagerChanged); + QObject::connect(activityInfo, &ActivityInfo::currentActivityChanged, q, [this]() { if (pagerType == VirtualDesktops && windowModels.count()) { for (auto windowModel : windowModels) { windowModel->setActivity(activityInfo->currentActivity()); } } } ); QObject::connect(virtualDesktopInfo, &VirtualDesktopInfo::desktopLayoutRowsChanged, q, &PagerModel::layoutRowsChanged); QObject::connect(desktopWidget, &QDesktopWidget::screenCountChanged, q, &PagerModel::pagerItemSizeChanged); QObject::connect(desktopWidget, &QDesktopWidget::resized, q, &PagerModel::pagerItemSizeChanged); #if HAVE_X11 QObject::connect(KWindowSystem::self(), &KWindowSystem::stackingOrderChanged, q, [this]() { cachedStackingOrder = KWindowSystem::stackingOrder(); for (auto windowModel : windowModels) { windowModel->refreshStackingOrder(); } } ); #endif } PagerModel::Private::~Private() { --instanceCount; if (!instanceCount) { delete activityInfo; activityInfo = nullptr; delete virtualDesktopInfo; virtualDesktopInfo = nullptr; } } void PagerModel::Private::refreshDataSource() { if (pagerType == VirtualDesktops) { QObject::disconnect(virtualDesktopNumberConn); virtualDesktopNumberConn = QObject::connect(virtualDesktopInfo, &VirtualDesktopInfo::numberOfDesktopsChanged, q, [this]() { q->refresh(); }); QObject::disconnect(virtualDesktopNamesConn); virtualDesktopNamesConn = QObject::connect(virtualDesktopInfo, &VirtualDesktopInfo::desktopNamesChanged, q, [this]() { if (q->rowCount()) { emit q->dataChanged(q->index(0, 0), q->index(q->rowCount() - 1, 0), QVector{Qt::DisplayRole}); } } ); QObject::disconnect(activityInfoConn); QObject::disconnect(activityInfo, &ActivityInfo::currentActivityChanged, q, &PagerModel::currentPageChanged); QObject::connect(virtualDesktopInfo, &VirtualDesktopInfo::currentDesktopChanged, q, &PagerModel::currentPageChanged, Qt::UniqueConnection); } else { activityInfoConn = QObject::connect(activityInfo, &ActivityInfo::numberOfRunningActivitiesChanged, q, [this]() { q->refresh(); }, Qt::UniqueConnection); QObject::disconnect(virtualDesktopNumberConn); QObject::disconnect(virtualDesktopNamesConn); QObject::disconnect(virtualDesktopInfo, &VirtualDesktopInfo::currentDesktopChanged, q, &PagerModel::currentPageChanged); QObject::connect(activityInfo, &ActivityInfo::currentActivityChanged, q, &PagerModel::currentPageChanged, Qt::UniqueConnection); } emit q->currentPageChanged(); } PagerModel::PagerModel(QObject *parent) : QAbstractListModel(parent) , d(new Private(this)) { d->tasksModel = new WindowTasksModel(this); } PagerModel::~PagerModel() { } QHash PagerModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); for (int i = 0; i < e.keyCount(); ++i) { roles.insert(e.value(i), e.key(i)); } return roles; } int PagerModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return d->windowModels.count(); } QVariant PagerModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= d->windowModels.count()) { return QVariant(); } if (role == Qt::DisplayRole) { if (d->pagerType == VirtualDesktops) { return d->virtualDesktopInfo->desktopNames().at(index.row()); } else { QString activityId = d->activityInfo->runningActivities().at(index.row()); return d->activityInfo->activityName(activityId); } } else if (role == TasksModel) { return QVariant::fromValue(d->windowModels.at(index.row())); } return QVariant(); } PagerModel::PagerType PagerModel::pagerType() const { return d->pagerType; } void PagerModel::setPagerType(PagerType type) { if (d->pagerType != type) { d->pagerType = type; refresh(); emit pagerTypeChanged(); + emit shouldShowPagerChanged(); } } bool PagerModel::enabled() const { return d->enabled; } void PagerModel::setEnabled(bool enabled) { if (enabled && !d->enabled) { refresh(); d->enabled = true; emit enabledChanged(); } else if (!enabled && d->enabled) { beginResetModel(); disconnect(d->activityInfoConn); disconnect(d->virtualDesktopNumberConn); disconnect(d->virtualDesktopNamesConn); qDeleteAll(d->windowModels); d->windowModels.clear(); endResetModel(); d->enabled = false; emit enabledChanged(); emit countChanged(); } } +bool PagerModel::shouldShowPager() const +{ + return (d->pagerType == VirtualDesktops) ? d->virtualDesktopInfo->numberOfDesktops() > 1 + : d->activityInfo->numberOfRunningActivities() > 1; +} + bool PagerModel::showDesktop() const { return d->showDesktop; } void PagerModel::setShowDesktop(bool show) { if (d->showDesktop != show) { d->showDesktop = show; emit showDesktopChanged(); } } bool PagerModel::showOnlyCurrentScreen() const { return d->showOnlyCurrentScreen; } void PagerModel::setShowOnlyCurrentScreen(bool show) { if (d->showOnlyCurrentScreen != show) { d->showOnlyCurrentScreen = show; if (d->screenGeometry.isValid()) { emit pagerItemSizeChanged(); refresh(); } emit showOnlyCurrentScreenChanged(); } } QRect PagerModel::screenGeometry() const { return d->screenGeometry; } void PagerModel::setScreenGeometry(const QRect &geometry) { if (d->screenGeometry != geometry) { d->screenGeometry = geometry; if (d->showOnlyCurrentScreen) { emit pagerItemSizeChanged(); refresh(); } emit showOnlyCurrentScreenChanged(); } } int PagerModel::currentPage() const { if (d->pagerType == VirtualDesktops) { return d->virtualDesktopInfo->currentDesktop(); } else { return d->activityInfo->runningActivities().indexOf(d->activityInfo->currentActivity()); } } int PagerModel::layoutRows() const { return qBound(1, d->virtualDesktopInfo->desktopLayoutRows(), d->virtualDesktopInfo->numberOfDesktops()); } QSize PagerModel::pagerItemSize() const { if (d->showOnlyCurrentScreen && d->screenGeometry.isValid()) { return d->screenGeometry.size(); } QRect totalRect; for (int i = 0; i < d->desktopWidget->screenCount(); ++i) { totalRect |= d->desktopWidget->screenGeometry(i); } return totalRect.size(); } #if HAVE_X11 QList PagerModel::stackingOrder() const { return d->cachedStackingOrder; } #endif void PagerModel::refresh() { + if (!d->componentComplete) { + return; + } + beginResetModel(); d->refreshDataSource(); int modelCount = d->windowModels.count(); const int modelsNeeded = ((d->pagerType == VirtualDesktops) ? d->virtualDesktopInfo->numberOfDesktops() : d->activityInfo->numberOfRunningActivities()); if (modelCount > modelsNeeded) { while (modelCount != modelsNeeded) { delete d->windowModels.takeLast(); --modelCount; } } else if (modelsNeeded > modelCount) { while (modelCount != modelsNeeded) { WindowModel *windowModel = new WindowModel(this); windowModel->setFilterSkipPager(true); windowModel->setFilterByVirtualDesktop(true); windowModel->setFilterByActivity(true); windowModel->setDemandingAttentionSkipsFilters(false); windowModel->setSourceModel(d->tasksModel); d->windowModels.append(windowModel); ++modelCount; } } if (d->pagerType == VirtualDesktops) { int virtualDesktop = 1; for (auto windowModel : d->windowModels) { windowModel->setVirtualDesktop(virtualDesktop); ++virtualDesktop; windowModel->setActivity(d->activityInfo->currentActivity()); } } else { int activityIndex = 0; const QStringList &runningActivities = d->activityInfo->runningActivities(); for (auto windowModel : d->windowModels) { windowModel->setVirtualDesktop(0); windowModel->setActivity(runningActivities.at(activityIndex)); ++activityIndex; } } for (auto windowModel : d->windowModels) { if (d->showOnlyCurrentScreen && d->screenGeometry.isValid()) { windowModel->setScreenGeometry(d->screenGeometry); windowModel->setFilterByScreen(true); } else { windowModel->setFilterByScreen(false); } } endResetModel(); emit countChanged(); } void PagerModel::moveWindow(int window, double x, double y, int targetItemId, int sourceItemId, qreal widthScaleFactor, qreal heightScaleFactor) { #if HAVE_X11 if (!KWindowSystem::isPlatformX11()) { return; } const WId windowId = (WId)window; QPointF dest(x / widthScaleFactor, y / heightScaleFactor); // Don't move windows to negative positions. dest = QPointF(qMax(dest.x(), qreal(0.0)), qMax(dest.y(), qreal(0.0))); // Use _NET_MOVERESIZE_WINDOW rather than plain move, so that the WM knows this is a pager request. NETRootInfo info(QX11Info::connection(), 0); const int flags = (0x20 << 12) | (0x03 << 8) | 1; // From tool, x/y, northwest gravity. if (!KWindowSystem::mapViewport()) { - KWindowInfo windowInfo(windowId, NET::WMDesktop | NET::WMState); + KWindowInfo windowInfo(windowId, NET::WMDesktop | NET::WMState, NET::WM2Activities); if (d->pagerType == VirtualDesktops) { if (!windowInfo.onAllDesktops()) { KWindowSystem::setOnDesktop(windowId, targetItemId + 1); } } else { const QStringList &runningActivities = d->activityInfo->runningActivities(); if (targetItemId < runningActivities.length()) { - KActivities::Controller activitiesController; - activitiesController.setCurrentActivity(runningActivities.at(targetItemId)); + const QString &newActivity = runningActivities.at(targetItemId); + QStringList activities = windowInfo.activities(); + + if (!activities.contains(newActivity)) { + activities.removeOne(runningActivities.at(sourceItemId)); + activities.append(newActivity); + KWindowSystem::setOnActivities(windowId, activities); + } } } // Only move the window if it is not full screen and if it is kept within the same desktop. // Moving when dropping between desktop is too annoying due to the small drop area. if (!(windowInfo.state() & NET::FullScreen) && (targetItemId == sourceItemId || windowInfo.onAllDesktops())) { const QPoint &d = dest.toPoint(); info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); } } else { // setOnDesktop() with viewports is also moving a window, and since it takes a moment // for the WM to do the move, there's a race condition with figuring out how much to move, // so do it only as one move. dest += KWindowSystem::desktopToViewport(targetItemId + 1, false); const QPoint &d = KWindowSystem::constrainViewportRelativePosition(dest.toPoint()); info.moveResizeWindowRequest(windowId, flags, d.x(), d.y(), 0, 0); } #else Q_UNUSED(window) Q_UNUSED(x) Q_UNUSED(y) Q_UNUSED(targetDesktop) Q_UNUSED(sourceDesktop) #endif } void PagerModel::changePage(int itemId) { #if HAVE_X11 if (!KWindowSystem::isPlatformX11()) { return; } const int targetId = (d->pagerType == VirtualDesktops) ? itemId + 1 : itemId; if (currentPage() == targetId) { if (d->showDesktop) { QDBusConnection::sessionBus().asyncCall(QDBusMessage::createMethodCall(QLatin1String("org.kde.plasmashell"), QLatin1String("/PlasmaShell"), QLatin1String("org.kde.PlasmaShell"), QLatin1String("toggleDashboard"))); } } else { if (d->pagerType == VirtualDesktops) { KWindowSystem::setCurrentDesktop(targetId); } else { const QStringList &runningActivities = d->activityInfo->runningActivities(); if (targetId < runningActivities.length()) { KActivities::Controller activitiesController; activitiesController.setCurrentActivity(runningActivities.at(targetId)); } } } #else Q_UNUSED(itemId) #endif } void PagerModel::drop(QMimeData *mimeData, int itemId) { if (!mimeData) { return; } #if HAVE_X11 if (KWindowSystem::isPlatformX11()) { bool ok; const QList &ids = TaskManager::XWindowTasksModel::winIdsFromMimeData(mimeData, &ok); if (!ok) { return; } if (d->pagerType == VirtualDesktops) { for (const auto &id : ids) { KWindowSystem::setOnDesktop(id, itemId + 1); } } else { QString newActivity; const QStringList &runningActivities = d->activityInfo->runningActivities(); if (itemId < runningActivities.length()) { newActivity = runningActivities.at(itemId); } + if (newActivity.isEmpty()) { + return; + } + for (const auto &id : ids) { - KWindowSystem::setOnDesktop(id, itemId + 1); + QStringList activities = KWindowInfo(id, 0, NET::WM2Activities).activities(); + + if (!activities.contains(newActivity)) { + KWindowSystem::setOnActivities(id, activities << newActivity); + } } } } #else Q_UNUSED(itemId) #endif } void PagerModel::addDesktop() { #if HAVE_X11 if (!KWindowSystem::isPlatformX11()) { return; } NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); info.setNumberOfDesktops(info.numberOfDesktops() + 1); #endif } void PagerModel::removeDesktop() { #if HAVE_X11 if (!KWindowSystem::isPlatformX11()) { return; } NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); if (info.numberOfDesktops() > 1) { info.setNumberOfDesktops(info.numberOfDesktops() - 1); } #endif } +void PagerModel::classBegin() +{ +} + +void PagerModel::componentComplete() +{ + d->componentComplete = true; + + if (d->enabled) { + refresh(); + } +} + #include "moc_pagermodel.cpp" diff --git a/applets/pager/plugin/pagermodel.h b/applets/pager/plugin/pagermodel.h index 56ba80388..2841e90f4 100644 --- a/applets/pager/plugin/pagermodel.h +++ b/applets/pager/plugin/pagermodel.h @@ -1,119 +1,129 @@ /******************************************************************** Copyright 2016 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 . *********************************************************************/ #ifndef PAGERMODEL_H #define PAGERMODEL_H #include #if HAVE_X11 #include #include #endif #include +#include class QMimeData; -class PagerModel : public QAbstractListModel +class PagerModel : public QAbstractListModel, public QQmlParserStatus { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_ENUMS(PagerType) Q_ENUMS(AdditionalRoles) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(PagerType pagerType READ pagerType WRITE setPagerType NOTIFY pagerTypeChanged) Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool shouldShowPager READ shouldShowPager NOTIFY shouldShowPagerChanged); Q_PROPERTY(bool showDesktop READ showDesktop WRITE setShowDesktop NOTIFY showDesktopChanged) Q_PROPERTY(bool showOnlyCurrentScreen READ showOnlyCurrentScreen WRITE setShowOnlyCurrentScreen NOTIFY showOnlyCurrentScreenChanged) Q_PROPERTY(QRect screenGeometry READ screenGeometry WRITE setScreenGeometry NOTIFY screenGeometryChanged) Q_PROPERTY(int currentPage READ currentPage NOTIFY currentPageChanged) Q_PROPERTY(int layoutRows READ layoutRows NOTIFY layoutRowsChanged) Q_PROPERTY(QSize pagerItemSize READ pagerItemSize NOTIFY pagerItemSizeChanged) public: enum PagerType { VirtualDesktops = 0, Activities }; enum AdditionalRoles { TasksModel = Qt::UserRole + 1 }; explicit PagerModel(QObject *parent = nullptr); virtual ~PagerModel(); QHash roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; PagerType pagerType() const; void setPagerType(PagerType type); bool enabled() const; void setEnabled(bool enabled); + bool shouldShowPager() const; + bool showDesktop() const; void setShowDesktop(bool show); bool showOnlyCurrentScreen() const; void setShowOnlyCurrentScreen(bool show); QRect screenGeometry() const; void setScreenGeometry(const QRect &geometry); int currentPage() const; int layoutRows() const; QSize pagerItemSize() const; #if HAVE_X11 QList stackingOrder() const; #endif Q_INVOKABLE void refresh(); Q_INVOKABLE void moveWindow(int window, double x, double y, int targetItemId, int sourceItemId, qreal widthScaleFactor, qreal heightScaleFactor); Q_INVOKABLE void changePage(int itemId); Q_INVOKABLE void drop(QMimeData *mimeData, int itemId); Q_INVOKABLE void addDesktop(); Q_INVOKABLE void removeDesktop(); + void classBegin(); + void componentComplete(); + Q_SIGNALS: void countChanged() const; void pagerTypeChanged() const; void enabledChanged() const; + void shouldShowPagerChanged() const; void showDesktopChanged() const; void showOnlyCurrentScreenChanged() const; void screenGeometryChanged() const; void currentPageChanged() const; void layoutRowsChanged() const; void pagerItemSizeChanged() const; private: class Private; QScopedPointer d; }; #endif diff --git a/applets/taskmanager/package/contents/code/tools.js b/applets/taskmanager/package/contents/code/tools.js index fcb4628c5..7c2e4600d 100644 --- a/applets/taskmanager/package/contents/code/tools.js +++ b/applets/taskmanager/package/contents/code/tools.js @@ -1,161 +1,169 @@ /*************************************************************************** * 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 . * ***************************************************************************/ -function wheelActivateNextPrevTask(wheelDelta, eventDelta) { +function wheelActivateNextPrevTask(anchor, wheelDelta, eventDelta) { // magic number 120 for common "one click" // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop wheelDelta += eventDelta; var increment = 0; while (wheelDelta >= 120) { wheelDelta -= 120; increment++; } while (wheelDelta <= -120) { wheelDelta += 120; increment--; } while (increment != 0) { - activateNextPrevTask(increment < 0) + activateNextPrevTask(anchor, increment < 0) increment += (increment < 0) ? 1 : -1; } return wheelDelta; } -function activateNextPrevTask(next) { +function activateNextPrevTask(anchor, next) { // FIXME TODO: Unnecessarily convoluted and costly; optimize. var taskIndexList = []; var activeTaskIndex = tasksModel.activeTask; for (var i = 0; i < taskList.children.length - 1; ++i) { var task = taskList.children[i]; var modelIndex = task.modelIndex(i); if (task.m.IsLauncher !== true && task.m.IsStartup !== true) { if (task.m.IsGroupParent === true) { + if (task == anchor) { // If the anchor is a group parent, collect only windows within the group. + taskIndexList = []; + } + for (var j = 0; j < tasksModel.rowCount(modelIndex); ++j) { taskIndexList.push(tasksModel.makeModelIndex(i, j)); } + + if (task == anchor) { // See above. + break; + } } else { taskIndexList.push(modelIndex); } } } if (!taskIndexList.length) { return; } var target = taskIndexList[0]; for (var i = 0; i < taskIndexList.length; ++i) { if (taskIndexList[i] === activeTaskIndex) { if (next && i < (taskIndexList.length - 1)) { target = taskIndexList[i + 1]; } else if (!next) { if (i) { target = taskIndexList[i - 1]; } else { target = taskIndexList[taskIndexList.length - 1]; } } break; } } tasksModel.requestActivate(target); } function activateTask(index, model, modifiers) { if (modifiers & Qt.ShiftModifier) { tasksModel.requestNewInstance(index); } else if (model.IsGroupParent === true) { if ((iconsOnly || modifiers == Qt.ControlModifier) && backend.canPresentWindows()) { toolTip.hideToolTip(); tasks.presentWindows(model.LegacyWinIdList); } else if (groupDialog.visible) { groupDialog.visible = false; } else { groupDialog.visualParent = task; groupDialog.visible = true; } } else { if (model.IsMinimized === true) { tasksModel.requestToggleMinimized(index); tasksModel.requestActivate(index); } else if (model.IsActive === true) { tasksModel.requestToggleMinimized(index); } else { tasksModel.requestActivate(index); } } } function insertIndexAt(above, x, y) { if (above) { return above.itemIndex; } else { var distance = tasks.vertical ? x : y; var step = tasks.vertical ? LayoutManager.taskWidth() : LayoutManager.taskHeight(); var stripe = Math.ceil(distance / step); if (stripe === LayoutManager.calculateStripes()) { return tasksModel.count - 1; } else { return stripe * LayoutManager.tasksPerStripe(); } } } function publishIconGeometries(taskItems) { for (var i = 0; i < taskItems.length - 1; ++i) { var task = taskItems[i]; if (task.IsLauncher !== true && task.m.IsStartup !== true) { tasksModel.requestPublishDelegateGeometry(tasksModel.makeModelIndex(task.itemIndex), backend.globalRect(task), task); } } } function taskPrefix(prefix) { var effectivePrefix; switch (plasmoid.location) { case PlasmaCore.Types.LeftEdge: effectivePrefix = "west-" + prefix; break; case PlasmaCore.Types.TopEdge: effectivePrefix = "north-" + prefix; break; case PlasmaCore.Types.RightEdge: effectivePrefix = "east-" + prefix; break; default: effectivePrefix = "south-" + prefix; } if (!frame.hasElementPrefix(effectivePrefix)) { return prefix; } return effectivePrefix; } diff --git a/applets/taskmanager/package/contents/ui/MouseHandler.qml b/applets/taskmanager/package/contents/ui/MouseHandler.qml index 741791e74..94fa8b095 100644 --- a/applets/taskmanager/package/contents/ui/MouseHandler.qml +++ b/applets/taskmanager/package/contents/ui/MouseHandler.qml @@ -1,153 +1,153 @@ /*************************************************************************** * 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 org.kde.draganddrop 2.0 import org.kde.taskmanager 0.1 as TaskManager import "../code/layout.js" as LayoutManager import "../code/tools.js" as TaskTools Item { signal urlsDropped(var urls) property Item target property Item ignoredItem property bool moved: false property alias hoveredItem: dropHandler.hoveredItem Timer { id: ignoreItemTimer repeat: false interval: 750 onTriggered: { ignoredItem = null; } } Connections { target: tasks onDragSourceChanged: { if (!dragSource) { ignoredItem = null; ignoreItemTimer.stop(); } } } DropArea { id: dropHandler anchors.fill: parent preventStealing: true; property Item hoveredItem onDragMove: { if (target.animating) { return; } var above = target.childAt(event.x, event.y); // If we're mixing launcher tasks with other tasks and are moving // a (small) launcher task across a non-launcher task, don't allow // the latter to be the move target twice in a row for a while, as // it will naturally be moved underneath the cursor as result of the // initial move, due to being far larger than the launcher delegate. // TODO: This restriction (minus the timer, which improves things) // has been proven out in the EITM fork, but could be improved later // by tracking the cursor movement vector and allowing the drag if // the movement direction has reversed, etablishing user intent to // move back. if (!plasmoid.configuration.separateLaunchers && tasks.dragSource != null && tasks.dragSource.m.IsLauncher === true && above != null && above.m.IsLauncher !== true && above == ignoredItem) { return; } else { ignoredItem = null; } if (tasksModel.sortMode == TaskManager.TasksModel.SortManual && tasks.dragSource) { var insertAt = TaskTools.insertIndexAt(above, event.x, event.y); if (!groupDialog.visible && tasks.dragSource != above && tasks.dragSource.itemIndex != insertAt) { tasksModel.move(tasks.dragSource.itemIndex, insertAt); ignoredItem = above; ignoreItemTimer.restart(); } } else if (!tasks.dragSource && above && hoveredItem != above) { hoveredItem = above; activationTimer.restart(); } else if (!above) { hoveredItem = null; activationTimer.stop(); } } onDragLeave: { hoveredItem = null; activationTimer.stop(); } onDrop: { // Reject internal drops. if (event.mimeData.formats.indexOf("application/x-orgkdeplasmataskmanager_taskbuttonitem") >= 0) { return; } if (event.mimeData.hasUrls) { parent.urlsDropped(event.mimeData.urls); } } Timer { id: activationTimer interval: 250 repeat: false onTriggered: { if (parent.hoveredItem.m.IsGroupParent === true) { groupDialog.visualParent = parent.hoveredItem; groupDialog.visible = true; } else if (parent.hoveredItem.m.IsLauncher !== true) { tasksModel.requestActivate(parent.hoveredItem.modelIndex()); } } } } MouseArea { id: wheelHandler anchors.fill: parent property int wheelDelta: 0; enabled: plasmoid.configuration.wheelEnabled - onWheel: wheelDelta = TaskTools.wheelActivateNextPrevTask(wheelDelta, wheel.angleDelta.y); + onWheel: wheelDelta = TaskTools.wheelActivateNextPrevTask(null, wheelDelta, wheel.angleDelta.y); } } diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml index f48728600..81d93f1cb 100644 --- a/applets/taskmanager/package/contents/ui/Task.qml +++ b/applets/taskmanager/package/contents/ui/Task.qml @@ -1,463 +1,463 @@ /*************************************************************************** * 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.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.draganddrop 2.0 import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet import "../code/layout.js" as LayoutManager import "../code/tools.js" as TaskTools MouseArea { id: task width: groupDialog.mainItem.width height: Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.small) + LayoutManager.verticalMargins() visible: false LayoutMirroring.enabled: (Qt.application.layoutDirection == Qt.RightToLeft) LayoutMirroring.childrenInherit: (Qt.application.layoutDirection == Qt.RightToLeft) readonly property var m: model property int itemIndex: index property bool inPopup: false property bool isWindow: model.IsWindow === true property alias textWidth: label.implicitWidth property bool pressed: false property int pressX: -1 property int pressY: -1 property QtObject contextMenu: null property int wheelDelta: 0 readonly property bool smartLauncherEnabled: plasmoid.configuration.smartLaunchersEnabled && !inPopup && model.IsStartup !== true property QtObject smartLauncherItem: null readonly property bool highlighted: (inPopup && activeFocus) || (!inPopup && containsMouse) acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MidButton onIsWindowChanged: { if (isWindow) { taskInitComponent.createObject(task); } } onItemIndexChanged: { if (!inPopup && !tasks.vertical && (LayoutManager.calculateStripes() > 1 || !plasmoid.configuration.separateLaunchers)) { tasks.requestLayout(); } } onContainsMouseChanged: { if (containsMouse) { if (inPopup) { forceActiveFocus() } } else { pressed = false; } if (model.IsWindow === true) { tasks.windowsHovered(model.LegacyWinIdList, containsMouse); } } onPressed: { if (mouse.button == Qt.LeftButton || mouse.button == Qt.MidButton) { pressed = true; pressX = mouse.x; pressY = mouse.y; } else if (mouse.button == Qt.RightButton) { if (plasmoid.configuration.showToolTips) { toolTip.hideToolTip(); } tasks.createContextMenu(task).show(); } } onReleased: { if (pressed) { if (mouse.button == Qt.MidButton) { if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.NewInstance) { tasksModel.requestNewInstance(modelIndex()); } else if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.Close) { tasksModel.requestClose(modelIndex()); } else if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.ToggleMinimized) { tasksModel.requestToggleMinimized(modelIndex()); } } else if (mouse.button == Qt.LeftButton) { TaskTools.activateTask(modelIndex(), model, mouse.modifiers); } } pressed = false; pressX = -1; pressY = -1; } onPositionChanged: { // mouse.button is always 0 here, hence checking with mouse.buttons if (pressX != -1 && mouse.buttons == Qt.LeftButton && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) { tasks.dragSource = task; dragHelper.startDrag(task, model.MimeType, model.MimeData, model.LauncherUrlWithoutIcon, model.decoration); pressX = -1; pressY = -1; return; } } onWheel: { if (plasmoid.configuration.wheelEnabled) { - wheelDelta = TaskTools.wheelActivateNextPrevTask(wheelDelta, wheel.angleDelta.y); + wheelDelta = TaskTools.wheelActivateNextPrevTask(task, wheelDelta, wheel.angleDelta.y); } else { wheel.accepted = false; } } onSmartLauncherEnabledChanged: { if (smartLauncherEnabled && !smartLauncherItem) { var smartLauncher = Qt.createQmlObject(" import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet; TaskManagerApplet.SmartLauncherItem { }", task); smartLauncher.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); smartLauncherItem = smartLauncher; } } Keys.onReturnPressed: TaskTools.activateTask(modelIndex(), model, event.modifiers) Keys.onEnterPressed: Keys.onReturnPressed(event); function modelIndex() { return (inPopup ? tasksModel.makeModelIndex(groupDialog.visualParent.itemIndex, index) : tasksModel.makeModelIndex(index)); } Component { id: taskInitComponent Timer { id: timer interval: units.longDuration * 2 repeat: false onTriggered: { parent.hoverEnabled = true; if (parent.isWindow) { tasksModel.requestPublishDelegateGeometry(parent.modelIndex(), backend.globalRect(parent), parent); } timer.destroy(); } Component.onCompleted: timer.start() } } PlasmaCore.FrameSvgItem { id: frame anchors { fill: parent topMargin: (!tasks.vertical && taskList.rows > 1) ? units.smallSpacing / 4 : 0 bottomMargin: (!tasks.vertical && taskList.rows > 1) ? units.smallSpacing / 4 : 0 leftMargin: ((inPopup || tasks.vertical) && taskList.columns > 1) ? units.smallSpacing / 4 : 0 rightMargin: ((inPopup || tasks.vertical) && taskList.columns > 1) ? units.smallSpacing / 4 : 0 } imagePath: "widgets/tasks" property string basePrefix: "normal" prefix: TaskTools.taskPrefix(basePrefix) onRepaintNeeded: updatePrefix() function updatePrefix() { prefix = Qt.binding(function() { return TaskTools.taskPrefix(basePrefix); }); } PlasmaCore.ToolTipArea { id: toolTip anchors.fill: parent active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips interactive: true location: plasmoid.location mainItem: toolTipDelegate onContainsMouseChanged: { if (containsMouse) { toolTipDelegate.parentIndex = itemIndex; toolTipDelegate.windows = Qt.binding(function() { return model.LegacyWinIdList; }); toolTipDelegate.mainText = Qt.binding(function() { return model.display; }); toolTipDelegate.icon = Qt.binding(function() { return model.decoration; }); toolTipDelegate.subText = Qt.binding(function() { return model.IsLauncher === true ? model.GenericName : toolTip.generateSubText(model); }); toolTipDelegate.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); } } function generateSubText(task) { var subTextEntries = new Array(); if (!plasmoid.configuration.showOnlyCurrentDesktop && virtualDesktopInfo.numberOfDesktops > 1 && model.IsOnAllVirtualDesktops !== true && model.VirtualDesktop != -1 && model.VirtualDesktop != undefined) { subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); } if (model.Activities == undefined) { return subTextEntries.join("\n"); } if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { subTextEntries.push(i18nc("Which virtual desktop a window is currently on", "Available on all activities")); } else if (model.Activities.length > 0) { var activityNames = new Array(); for (var i = 0; i < model.Activities.length; i++) { var activity = model.Activities[i]; if (plasmoid.configuration.showOnlyCurrentActivity) { if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } else if (activity != activityInfo.currentActivity) { activityNames.push(activityInfo.activityName(model.Activities[i])); } } if (plasmoid.configuration.showOnlyCurrentActivity) { if (activityNames.length > 0) { subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", "Also available on %1", activityNames.join(", "))); } } else if (activityNames.length > 0) { subTextEntries.push(i18nc("Which activities a window is currently on", "Available on %1", activityNames.join(", "))); } } return subTextEntries.join("\n"); } } } Loader { anchors.fill: frame asynchronous: true source: "TaskProgressOverlay.qml" active: plasmoid.configuration.smartLaunchersEnabled && task.smartLauncherItem && task.smartLauncherItem.progressVisible } Item { id: iconBox anchors { left: parent.left leftMargin: adjustMargin(true, parent.width, taskFrame.margins.left) top: parent.top topMargin: adjustMargin(false, parent.height, taskFrame.margins.top) } width: (label.visible ? height : parent.width - adjustMargin(true, parent.width, taskFrame.margins.left) - adjustMargin(true, parent.width, taskFrame.margins.right)) height: (parent.height - adjustMargin(false, parent.height, taskFrame.margins.top) - adjustMargin(false, parent.height, taskFrame.margins.bottom)) function adjustMargin(vert, size, margin) { if (!size) { return margin; } var margins = vert ? LayoutManager.horizontalMargins() : LayoutManager.verticalMargins(); if ((size - margins) < units.iconSizes.small) { return Math.ceil((margin * (units.iconSizes.small / size)) / 2); } return margin; } //width: inPopup ? units.iconSizes.small : Math.min(height, parent.width - LayoutManager.horizontalMargins()) PlasmaCore.IconItem { id: icon anchors.fill: parent active: task.highlighted || (task.contextMenu && task.contextMenu.status == PlasmaComponents.DialogStatus.Open) enabled: true usesPlasmaTheme: false source: model.decoration } Loader { anchors.fill: icon asynchronous: true source: "TaskBadgeOverlay.qml" active: plasmoid.configuration.smartLaunchersEnabled && height >= units.iconSizes.small && task.smartLauncherItem && task.smartLauncherItem.countVisible } states: [ // Using a state transition avoids a binding loop between label.visible and // the text label margin, which derives from the icon width. State { name: "standalone" when: !label.visible AnchorChanges { target: iconBox anchors.left: undefined anchors.horizontalCenter: parent.horizontalCenter } PropertyChanges { target: iconBox anchors.leftMargin: 0 } } ] Loader { anchors.fill: parent active: model.IsStartup === true sourceComponent: busyIndicator } Component { id: busyIndicator PlasmaComponents.BusyIndicator { anchors.fill: parent } } } PlasmaComponents.Label { id: label visible: (inPopup || !iconsOnly && model.IsLauncher !== true && (parent.width - iconBox.height - units.smallSpacing) >= (theme.mSize(theme.defaultFont).width * 7)) anchors { fill: parent leftMargin: taskFrame.margins.left + iconBox.width + units.smallSpacing topMargin: taskFrame.margins.top rightMargin: taskFrame.margins.right bottomMargin: taskFrame.margins.bottom } text: model.display wrapMode: Text.Wrap elide: Text.ElideRight textFormat: Text.PlainText verticalAlignment: Text.AlignVCenter } states: [ State { name: "launcher" when: model.IsLauncher === true PropertyChanges { target: frame basePrefix: "" } }, State { name: "hovered" when: task.highlighted || (contextMenu.status == PlasmaComponents.DialogStatus.Open && contextMenu.visualParent == task) PropertyChanges { target: frame basePrefix: "hover" } }, State { name: "attention" when: model.IsDemandingAttention === true || (task.smartLauncherItem && task.smartLauncherItem.urgent) PropertyChanges { target: frame basePrefix: "attention" } }, State { name: "minimized" when: model.IsMinimized === true && !(groupDialog.visible && groupDialog.visualParent == task) PropertyChanges { target: frame basePrefix: "minimized" } }, State { name: "active" when: model.IsActive === true || groupDialog.visible && groupDialog.visualParent == task PropertyChanges { target: frame basePrefix: "focus" } } ] Component.onCompleted: { if (!inPopup && model.IsWindow === true) { var component = Qt.createComponent("GroupExpanderOverlay.qml"); component.createObject(task); } if (!inPopup && model.IsWindow !== true) { taskInitComponent.createObject(task); } } }