From efe439e2bc95cf5dcf8990900f2fe02c89b924ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 27 Nov 2011 14:05:44 +0100 Subject: [PATCH 01/16] Include WindowStrip as TabBox layout --- CMakeLists.txt | 1 + tabbox/qml/window_strip.qml | 150 ++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 tabbox/qml/window_strip.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index d1461e3563..d75cf14e78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,6 +257,7 @@ install( FILES tabbox/qml/text.qml tabbox/qml/thumbnails.qml tabbox/qml/IconTabBox.qml + tabbox/qml/window_strip.qml DESTINATION ${DATA_INSTALL_DIR}/kwin/tabbox ) kde4_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml new file mode 100644 index 0000000000..9cc868f592 --- /dev/null +++ b/tabbox/qml/window_strip.qml @@ -0,0 +1,150 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 Martin Gräßlin +Copyright (C) 2011 Marco Martin + +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 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.plasma.components 0.1 as PlasmaComponents +import org.kde.plasma.mobilecomponents 0.1 as MobileComponents +import org.kde.qtextracomponents 0.1 +import org.kde.kwin 0.1 as KWin + +Item { + id: thumbnailTabBox + property int screenWidth + property bool canStretchX: false + property bool canStretchY: false + width: screenWidth + height: 150 + clip: true + + + function setModel(model) { + thumbnailListView.model = model; + } + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + PlasmaCore.Theme { + id: theme + } + PlasmaCore.Svg { + id: iconsSvg + imagePath: "widgets/configuration-icons" + } + + ListView { + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return thumbnailListView.indexAt(pos.x, pos.y); + } + signal currentIndexChanged(int index) + id: thumbnailListView + objectName: "listView" + orientation: ListView.Horizontal + height: parent.height + spacing: 10 + anchors.fill: parent + clip: true + delegate: Item { + id: delegateItem + width: thumbnailListView.height * 1.6 + 48 + height: thumbnailListView.height + KWin.ThumbnailItem { + id: thumbnailItem + wId: windowId + width: parent.width - closeButtonContainer.width - 20 + height: thumbnailListView.height - 40 + anchors.centerIn: parent + MouseArea { + anchors.fill: parent + onClicked: { + thumbnailListView.currentIndex = index + thumbnailListView.currentIndexChanged(thumbnailListView.currentIndex); + } + } + } + PlasmaComponents.Label { + id: windowTitle + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter; + anchors.topMargin: 4 + + text: caption + elide: Text.ElideRight + color: theme.textColor + width: parent.width - 40 + horizontalAlignment: Text.AlignHCenter + } + PlasmaCore.FrameSvgItem { + id: closeButtonContainer + imagePath: "widgets/button" + prefix: "shadow" + width: closeButton.width + margins.left + margins.right + height: closeButton.height + margins.top + margins.bottom + visible: true//model["actionClose"] && (model["className"] != shellName) + anchors { + top: parent.top + right: parent.right + topMargin: 32 + } + + PlasmaCore.FrameSvgItem { + id: closeButton + imagePath: "widgets/button" + prefix: "normal" + //a bit more left margin + width: closeButtonSvg.width + margins.left + margins.right + 16 + height: closeButtonSvg.height + margins.top + margins.bottom + x: parent.margins.left + y: parent.margins.top + + MobileComponents.ActionButton { + id: closeButtonSvg + svg: iconsSvg + iconSize: 22 + backgroundVisible: false + elementId: "close" + + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: parent.margins.right + } + + onClicked: { + console.log("Close button clicked") + /*var service = tasksSource.serviceForSource(winId) + var operation = service.operationDescription("close") + + service.startOperationCall(operation)*/ + } + } + } + } + } + } +} From 9eb5a17e3093d6b5a8edbd1de566a8b3f220b88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 27 Nov 2011 14:15:49 +0100 Subject: [PATCH 02/16] Support Closeable in TabBox Needed by window strip to not add a close button to non-closeable windows such as the desktop shell. --- tabbox/clientmodel.cpp | 3 +++ tabbox/clientmodel.h | 3 ++- tabbox/qml/window_strip.qml | 2 +- tabbox/tabbox.cpp | 5 +++++ tabbox/tabbox.h | 1 + tabbox/tabboxhandler.h | 1 + 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index eea8c91726..361d3d20ff 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -43,6 +43,7 @@ ClientModel::ClientModel(QObject* parent) roles[DesktopNameRole] = "desktopName"; roles[MinimizedRole] = "minimized"; roles[WIdRole] = "windowId"; + roles[CloseableRole] = "closeable"; setRoleNames(roles); } @@ -80,6 +81,8 @@ QVariant ClientModel::data(const QModelIndex& index, int role) const return qulonglong(m_clientList[ clientIndex ]->window()); case MinimizedRole: return m_clientList[ clientIndex ]->isMinimized(); + case CloseableRole: + return m_clientList[ clientIndex ]->isCloseable(); default: return QVariant(); } diff --git a/tabbox/clientmodel.h b/tabbox/clientmodel.h index acf81dd0ea..fb53ae371e 100644 --- a/tabbox/clientmodel.h +++ b/tabbox/clientmodel.h @@ -54,7 +54,8 @@ public: IconRole = Qt::UserRole + 3, // TODO: to be removed EmptyRole = Qt::UserRole + 4, ///< Indicates if the model contains TabBoxClients WIdRole = Qt::UserRole + 5, ///< The window ID of TabBoxClient - MinimizedRole = Qt::UserRole + 6 ///< TabBoxClient is minimized + MinimizedRole = Qt::UserRole + 6, ///< TabBoxClient is minimized + CloseableRole = Qt::UserRole + 7 ///< TabBoxClient can be closed }; ClientModel(QObject* parent = 0); ~ClientModel(); diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml index 9cc868f592..8abe7609ab 100644 --- a/tabbox/qml/window_strip.qml +++ b/tabbox/qml/window_strip.qml @@ -105,7 +105,7 @@ Item { prefix: "shadow" width: closeButton.width + margins.left + margins.right height: closeButton.height + margins.top + margins.bottom - visible: true//model["actionClose"] && (model["className"] != shellName) + visible: closeable anchors { top: parent.top right: parent.right diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 9cc7935875..09e98b3a97 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -270,6 +270,11 @@ int TabBoxClientImpl::height() const return m_client->height(); } +bool TabBoxClientImpl::isCloseable() const +{ + return m_client->isCloseable(); +} + /********************************************************* * TabBox diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 6ab07d69d3..27e3d908dd 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -80,6 +80,7 @@ public: virtual int y() const; virtual int width() const; virtual int height() const; + virtual bool isCloseable() const; Client* client() const { return m_client; diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index bf9fecdee9..ed4bb9a366 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -384,6 +384,7 @@ public: virtual int y() const = 0; virtual int width() const = 0; virtual int height() const = 0; + virtual bool isCloseable() const = 0; }; /** From 245f84d835394a7c46eed58553de65bb9f95d408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 27 Nov 2011 15:00:09 +0100 Subject: [PATCH 03/16] Close windows from TabBox QML Model exposes a method to close a window by index which is invoked by the window strip QML. --- tabbox/clientmodel.cpp | 9 +++++++++ tabbox/clientmodel.h | 4 ++++ tabbox/qml/window_strip.qml | 8 +------- tabbox/tabbox.cpp | 4 ++++ tabbox/tabbox.h | 1 + tabbox/tabboxhandler.h | 1 + 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index 361d3d20ff..541d957b0d 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -235,5 +235,14 @@ void ClientModel::createClientList(int desktop, bool partialReset) reset(); } +void ClientModel::close(int i) +{ + QModelIndex ind = index(i, 0); + if (!ind.isValid()) { + return; + } + m_clientList.at(i)->close(); +} + } // namespace Tabbox } // namespace KWin diff --git a/tabbox/clientmodel.h b/tabbox/clientmodel.h index fb53ae371e..c091c7c096 100644 --- a/tabbox/clientmodel.h +++ b/tabbox/clientmodel.h @@ -46,6 +46,7 @@ namespace TabBox class ClientModel : public QAbstractItemModel { + Q_OBJECT public: enum { ClientRole = Qt::UserRole, ///< The TabBoxClient @@ -95,6 +96,9 @@ public: return m_clientList; } +public Q_SLOTS: + void close(int index); + private: TabBoxClientList m_clientList; }; diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml index 8abe7609ab..f735061db4 100644 --- a/tabbox/qml/window_strip.qml +++ b/tabbox/qml/window_strip.qml @@ -135,13 +135,7 @@ Item { rightMargin: parent.margins.right } - onClicked: { - console.log("Close button clicked") - /*var service = tasksSource.serviceForSource(winId) - var operation = service.operationDescription("close") - - service.startOperationCall(operation)*/ - } + onClicked: thumbnailListView.model.close(index) } } } diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 09e98b3a97..31080f8ac8 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -275,6 +275,10 @@ bool TabBoxClientImpl::isCloseable() const return m_client->isCloseable(); } +void TabBoxClientImpl::close() +{ + m_client->closeWindow(); +} /********************************************************* * TabBox diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 27e3d908dd..41b9243453 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -81,6 +81,7 @@ public: virtual int width() const; virtual int height() const; virtual bool isCloseable() const; + virtual void close(); Client* client() const { return m_client; diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index ed4bb9a366..6b67f497c1 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -385,6 +385,7 @@ public: virtual int width() const = 0; virtual int height() const = 0; virtual bool isCloseable() const = 0; + virtual void close() = 0; }; /** From d2c7123dc6065a510792ded36b2cd3eda9717415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 21 Aug 2011 10:44:09 +0200 Subject: [PATCH 04/16] TabBox becomes activatable through a DBus interface Therefore TabBox is changed to be controlled without pressing a modifier key. Tab and Backtab are valid keys now which can be used to navigate in the list and return, enter and space can be used to close the box (and select the client). The TabBox is exported as object /TabBox on the kwin interface providing start and close methods and signal when the TabBox got closed. --- tabbox/tabbox.cpp | 41 ++++++++++++++++++++++++++++++++++++++++ tabbox/tabbox.h | 11 +++++++++-- tabbox/tabboxhandler.cpp | 2 ++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 31080f8ac8..992b377c6d 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -34,6 +34,7 @@ along with this program. If not, see . // Qt #include #include +#include // KDE #include #include @@ -288,6 +289,7 @@ TabBox::TabBox(QObject *parent) , m_displayRefcount(0) , m_desktopGrab(false) , m_tabGrab(false) + , m_noModifierGrab(false) , m_forcedGlobalMouseGrab(false) , m_ready(false) { @@ -323,10 +325,12 @@ TabBox::TabBox(QObject *parent) m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); connect(Workspace::self(), SIGNAL(configChanged()), this, SLOT(reconfigure())); + QDBusConnection::sessionBus().registerObject("/TabBox", this, QDBusConnection::ExportScriptableContents); } TabBox::~TabBox() { + QDBusConnection::sessionBus().unregisterObject("/TabBox"); } void TabBox::handlerReady() @@ -668,6 +672,19 @@ void TabBox::grabbedKeyEvent(QKeyEvent* event) // tabbox has been replaced, check effects return; } + if (m_noModifierGrab) { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) { + Client* c = currentClient(); + close(); + if (c) { + Workspace::self()->activateClient(c); + if (c->isShade() && options->shadeHover) + c->setShade(ShadeActivated); + if (c->isDesktop()) + Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); + } + } + } setCurrentIndex(m_tabBox->grabbedKeyEvent(event)); } @@ -922,11 +939,27 @@ void TabBox::modalActionsSwitch(bool enabled) action->setEnabled(enabled); } +void TabBox::start() +{ + if (isDisplayed()) { + return; + } + if (!establishTabBoxGrab()) { + return; + } + m_tabGrab = true; + m_noModifierGrab = true; + setMode(TabBoxWindowsMode); + reset(); + show(); +} + bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_tabGrab = true; + m_noModifierGrab = false; modalActionsSwitch(false); setMode(mode); reset(); @@ -938,6 +971,7 @@ bool TabBox::startWalkThroughDesktops(TabBoxMode mode) if (!establishTabBoxGrab()) return false; m_desktopGrab = true; + m_noModifierGrab = false; modalActionsSwitch(false); setMode(mode); reset(); @@ -1100,11 +1134,15 @@ void TabBox::keyPress(int keyQt) void TabBox::close(bool abort) { + if (!isGrabbed()) { + return; + } removeTabBoxGrab(); hide(abort); modalActionsSwitch(true); m_tabGrab = false; m_desktopGrab = false; + m_noModifierGrab = false; } /*! @@ -1112,6 +1150,9 @@ void TabBox::close(bool abort) */ void TabBox::keyRelease(const XKeyEvent& ev) { + if (m_noModifierGrab) { + return; + } unsigned int mk = ev.state & (KKeyServer::modXShift() | KKeyServer::modXCtrl() | diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 41b9243453..e7018aa225 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -97,6 +97,7 @@ private: class TabBox : public QObject { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin") public: TabBox(QObject *parent = NULL); ~TabBox(); @@ -162,12 +163,16 @@ public: int previousDesktopFocusChain(int iDesktop) const; int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; - void close(bool abort = false); void keyPress(int key); void keyRelease(const XKeyEvent& ev); public slots: void show(); + /** + * Only for DBus Interface to start primary KDE Walk through windows. + **/ + Q_SCRIPTABLE void start(); + Q_SCRIPTABLE void close(bool abort = false); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); @@ -192,7 +197,7 @@ public slots: signals: void tabBoxAdded(int); - void tabBoxClosed(); + Q_SCRIPTABLE void tabBoxClosed(); void tabBoxUpdated(); void tabBoxKeyEvent(QKeyEvent*); @@ -237,6 +242,8 @@ private: bool m_isShown; bool m_desktopGrab; bool m_tabGrab; + // true if tabbox is in modal mode which does not require holding a modifier + bool m_noModifierGrab; KShortcut m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse; KShortcut m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse; KShortcut m_cutWalkThroughWindows, m_cutWalkThroughWindowsReverse; diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index a03dd5fe02..ac05d2670e 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -586,11 +586,13 @@ QModelIndex TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const if (column >= model->columnCount()) column = 0; break; + case Qt::Key_Backtab: case Qt::Key_Up: row--; if (row < 0) row = model->rowCount() - 1; break; + case Qt::Key_Tab: case Qt::Key_Down: row++; if (row >= model->rowCount()) From 5a7120fbf5743fdfe93737a78a8b244d8256ef56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 21 Aug 2011 11:14:34 +0200 Subject: [PATCH 05/16] Non-modal TabBox mode Available through DBus. Be aware that this does not make much sense, as there is no way to end TabBox mode except through the DBus interface and this does not yet select the selected window. --- tabbox/tabbox.cpp | 17 ++++++++++------- tabbox/tabbox.h | 4 +++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 992b377c6d..3c17fcbccb 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -939,15 +939,19 @@ void TabBox::modalActionsSwitch(bool enabled) action->setEnabled(enabled); } -void TabBox::start() +void TabBox::start(bool modal) { if (isDisplayed()) { return; } - if (!establishTabBoxGrab()) { - return; + if (modal) { + if (!establishTabBoxGrab()) { + return; + } + m_tabGrab = true; + } else { + m_tabGrab = false; } - m_tabGrab = true; m_noModifierGrab = true; setMode(TabBoxWindowsMode); reset(); @@ -1134,10 +1138,9 @@ void TabBox::keyPress(int keyQt) void TabBox::close(bool abort) { - if (!isGrabbed()) { - return; + if (isGrabbed()) { + removeTabBoxGrab(); } - removeTabBoxGrab(); hide(abort); modalActionsSwitch(true); m_tabGrab = false; diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index e7018aa225..a061203bbe 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -170,8 +170,10 @@ public slots: void show(); /** * Only for DBus Interface to start primary KDE Walk through windows. + * @param modal Whether the TabBox should grab keyboard and mouse, that is go into modal + * mode or whether the TabBox is controlled externally (e.g. through an effect). **/ - Q_SCRIPTABLE void start(); + Q_SCRIPTABLE void start(bool modal = true); Q_SCRIPTABLE void close(bool abort = false); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); From e858b413a37178f7b5e2faf42a30debcb1aebc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Sun, 27 Nov 2011 17:03:35 +0100 Subject: [PATCH 06/16] Activate TabBoxClient from mouse click and close TabBox This is useful in combination of non-modal TabBox through DBus and window-strip layout. With DBus the non-modal TabBox can be started and selecting any item on the strip will end TabBox again. --- tabbox/clientmodel.cpp | 10 ++++++++++ tabbox/clientmodel.h | 5 +++++ tabbox/qml/window_strip.qml | 5 +---- tabbox/tabbox.cpp | 16 ++++++++++++++++ tabbox/tabbox.h | 1 + tabbox/tabboxhandler.h | 4 ++++ 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index 541d957b0d..92c748dbb5 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -244,5 +244,15 @@ void ClientModel::close(int i) m_clientList.at(i)->close(); } +void ClientModel::activate(int i) +{ + QModelIndex ind = index(i, 0); + if (!ind.isValid()) { + return; + } + tabBox->setCurrentIndex(ind); + tabBox->activateAndClose(); +} + } // namespace Tabbox } // namespace KWin diff --git a/tabbox/clientmodel.h b/tabbox/clientmodel.h index c091c7c096..450d66515c 100644 --- a/tabbox/clientmodel.h +++ b/tabbox/clientmodel.h @@ -98,6 +98,11 @@ public: public Q_SLOTS: void close(int index); + /** + * Activates the client at @p index and closes the TabBox. + * @param index The row index + **/ + void activate(int index); private: TabBoxClientList m_clientList; diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml index f735061db4..3ce0f49cd4 100644 --- a/tabbox/qml/window_strip.qml +++ b/tabbox/qml/window_strip.qml @@ -81,10 +81,7 @@ Item { anchors.centerIn: parent MouseArea { anchors.fill: parent - onClicked: { - thumbnailListView.currentIndex = index - thumbnailListView.currentIndexChanged(thumbnailListView.currentIndex); - } + onClicked: thumbnailListView.model.activate(index) } } PlasmaComponents.Label { diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 3c17fcbccb..eaa6e53a53 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -214,6 +214,22 @@ QVector< Window > TabBoxHandlerImpl::outlineWindowIds() const return Workspace::self()->outline()->windowIds(); } +void TabBoxHandlerImpl::activateAndClose() +{ + Client* c = NULL; + if (TabBoxClientImpl* cl = static_cast< TabBoxClientImpl* >(client(currentIndex()))) { + c = cl->client(); + } + m_tabBox->close(); + if (c) { + Workspace::self()->activateClient(c); + if (c->isShade() && options->shadeHover) + c->setShade(ShadeActivated); + if (c->isDesktop()) + Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); + } +} + /********************************************************* * TabBoxClientImpl diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index a061203bbe..51a004cac2 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -61,6 +61,7 @@ public: virtual void hideOutline(); virtual void showOutline(const QRect &outline); virtual QVector< Window > outlineWindowIds() const; + virtual void activateAndClose(); private: TabBox* m_tabBox; diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index 6b67f497c1..1355674461 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -173,6 +173,10 @@ public: * @return The first desktop window in the stacking order. */ virtual TabBoxClient* desktopClient() const = 0; + /** + * Activates the currently selected client and closes the TabBox. + **/ + virtual void activateAndClose() = 0; /** * @return The currently used TabBoxConfig From cc6fa14c9b1004aaf0e6eb10143c16283e955a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 29 Nov 2011 07:05:41 +0100 Subject: [PATCH 07/16] API improvements: open/close instead of start/close --- tabbox/tabbox.cpp | 2 +- tabbox/tabbox.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index eaa6e53a53..42f007cfc9 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -955,7 +955,7 @@ void TabBox::modalActionsSwitch(bool enabled) action->setEnabled(enabled); } -void TabBox::start(bool modal) +void TabBox::open(bool modal) { if (isDisplayed()) { return; diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 51a004cac2..60fd4009d2 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -174,7 +174,7 @@ public slots: * @param modal Whether the TabBox should grab keyboard and mouse, that is go into modal * mode or whether the TabBox is controlled externally (e.g. through an effect). **/ - Q_SCRIPTABLE void start(bool modal = true); + Q_SCRIPTABLE void open(bool modal = true); Q_SCRIPTABLE void close(bool abort = false); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); From b57ef77bf7bf9e8afa55343d74dfc1d650aaf16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 29 Nov 2011 07:13:26 +0100 Subject: [PATCH 08/16] Adding a clip property to the ThumbnailItem By default clip is enabled. This means that if the thumbnail would overlap the "parent" window, it does not get rendered at all. If set to false it will always be rendered. This is required for window strip where the complete screen width is used, so overlap does not matter. --- scene.cpp | 4 ++-- tabbox/qml/window_strip.qml | 1 + thumbnailitem.cpp | 7 +++++++ thumbnailitem.h | 7 +++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/scene.cpp b/scene.cpp index 81ca9378a8..82935c2e90 100644 --- a/scene.cpp +++ b/scene.cpp @@ -419,8 +419,8 @@ void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quad } else { thumbMask |= PAINT_WINDOW_TRANSLUCENT; } - if (x < wImpl->x() || x + size.width() > wImpl->x() + wImpl->width() || - y < wImpl->y() || y + size.height() > wImpl->y() + wImpl->height()) { + if (item->isClip() && (x < wImpl->x() || x + size.width() > wImpl->x() + wImpl->width() || + y < wImpl->y() || y + size.height() > wImpl->y() + wImpl->height())) { // don't render windows outside the containing window. // TODO: improve by spliting out the window quads which do not fit continue; diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml index 3ce0f49cd4..801bc61158 100644 --- a/tabbox/qml/window_strip.qml +++ b/tabbox/qml/window_strip.qml @@ -78,6 +78,7 @@ Item { wId: windowId width: parent.width - closeButtonContainer.width - 20 height: thumbnailListView.height - 40 + clip: false anchors.centerIn: parent MouseArea { anchors.fill: parent diff --git a/thumbnailitem.cpp b/thumbnailitem.cpp index 8c209833f0..73441a059f 100644 --- a/thumbnailitem.cpp +++ b/thumbnailitem.cpp @@ -35,6 +35,7 @@ namespace KWin ThumbnailItem::ThumbnailItem(QDeclarativeItem* parent) : QDeclarativeItem(parent) , m_wId(0) + , m_clip(true) , m_parent(QWeakPointer()) { setFlags(flags() & ~QGraphicsItem::ItemHasNoContents); @@ -81,6 +82,12 @@ void ThumbnailItem::setWId(qulonglong wId) emit wIdChanged(wId); } +void ThumbnailItem::setClip(bool clip) +{ + m_clip = clip; + emit clipChanged(clip); +} + void ThumbnailItem::effectWindowAdded() { // the window might be added before the EffectWindow is created diff --git a/thumbnailitem.h b/thumbnailitem.h index 2ff354672f..8d39f5fc26 100644 --- a/thumbnailitem.h +++ b/thumbnailitem.h @@ -34,6 +34,7 @@ class ThumbnailItem : public QDeclarativeItem { Q_OBJECT Q_PROPERTY(qulonglong wId READ wId WRITE setWId NOTIFY wIdChanged SCRIPTABLE true) + Q_PROPERTY(bool clip READ isClip WRITE setClip NOTIFY clipChanged SCRIPTABLE true) public: ThumbnailItem(QDeclarativeItem *parent = 0); virtual ~ThumbnailItem(); @@ -42,15 +43,21 @@ public: return m_wId; } void setWId(qulonglong wId); + bool isClip() const { + return m_clip; + } + void setClip(bool clip); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); Q_SIGNALS: void wIdChanged(qulonglong wid); + void clipChanged(bool clipped); private Q_SLOTS: void init(); void effectWindowAdded(); private: void findParentEffectWindow(); qulonglong m_wId; + bool m_clip; QWeakPointer m_parent; }; From 5eb5a60cc5a33deed66a5df65c977b4b55507dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 1 Dec 2011 08:48:18 +0100 Subject: [PATCH 09/16] Embedded mode for TabBox DBus method to embedd the TabBox into another window. It follows the geometry changes and keeps a defined offset to the borders of the parent window. Required for Plasma Active's window strip. --- tabbox/declarative.cpp | 96 ++++++++++++++++++++++++++++++++++++---- tabbox/declarative.h | 9 +++- tabbox/tabbox.cpp | 17 +++++++ tabbox/tabbox.h | 16 +++++++ tabbox/tabboxhandler.cpp | 59 ++++++++++++++++++++++++ tabbox/tabboxhandler.h | 11 +++++ 6 files changed, 198 insertions(+), 10 deletions(-) diff --git a/tabbox/declarative.cpp b/tabbox/declarative.cpp index f7a9c989df..3828f1a3f0 100644 --- a/tabbox/declarative.cpp +++ b/tabbox/declarative.cpp @@ -39,6 +39,7 @@ along with this program. If not, see . #include // KWin #include "thumbnailitem.h" +#include namespace KWin { @@ -104,10 +105,16 @@ DeclarativeView::DeclarativeView(QAbstractItemModel *model, QWidget *parent) , m_currentScreenGeometry() , m_frame(new Plasma::FrameSvg(this)) , m_currentLayout() + , m_cachedWidth(0) + , m_cachedHeight(0) { setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::X11BypassWindowManagerHint); - setResizeMode(QDeclarativeView::SizeViewToRootObject); + if (tabBox->embedded()) { + setResizeMode(QDeclarativeView::SizeRootObjectToView); + } else { + setResizeMode(QDeclarativeView::SizeViewToRootObject); + } QPalette pal = palette(); pal.setColor(backgroundRole(), Qt::transparent); setPalette(pal); @@ -130,10 +137,14 @@ DeclarativeView::DeclarativeView(QAbstractItemModel *model, QWidget *parent) m_frame->setEnabledBorders(Plasma::FrameSvg::AllBorders); connect(tabBox, SIGNAL(configChanged()), SLOT(updateQmlSource())); + connect(tabBox, SIGNAL(embeddedChanged(bool)), SLOT(slotEmbeddedChanged(bool))); } void DeclarativeView::showEvent(QShowEvent *event) { + if (tabBox->embedded()) { + connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,uint)), SLOT(slotWindowChanged(WId, uint))); + } updateQmlSource(); m_currentScreenGeometry = Kephal::ScreenUtils::screenGeometry(tabBox->activeScreen()); rootObject()->setProperty("screenWidth", m_currentScreenGeometry.width()); @@ -154,10 +165,12 @@ void DeclarativeView::showEvent(QShowEvent *event) void DeclarativeView::resizeEvent(QResizeEvent *event) { m_frame->resizeFrame(event->size()); - if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) { + if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled() && !tabBox->embedded()) { // blur background Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame->mask()); Plasma::WindowEffects::overrideShadow(winId(), true); + } else if (tabBox->embedded()) { + Plasma::WindowEffects::enableBlurBehind(winId(), false); } else { // do not trim to mask with compositing enabled, otherwise shadows are cropped setMask(m_frame->mask()); @@ -165,13 +178,51 @@ void DeclarativeView::resizeEvent(QResizeEvent *event) QDeclarativeView::resizeEvent(event); } +void DeclarativeView::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + if (tabBox->embedded()) { + disconnect(KWindowSystem::self(), SIGNAL(windowChanged(WId,uint)), this, SLOT(slotWindowChanged(WId,uint))); + } +} + void DeclarativeView::slotUpdateGeometry() { - const int width = rootObject()->property("width").toInt(); - const int height = rootObject()->property("height").toInt(); - setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, - m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, - width, height); + const WId embeddedId = tabBox->embedded(); + if (embeddedId != 0) { + const KWindowInfo info = KWindowSystem::windowInfo(embeddedId, NET::WMGeometry); + const Qt::Alignment alignment = tabBox->embeddedAlignment(); + const QPoint offset = tabBox->embeddedOffset(); + int x = info.geometry().left(); + int y = info.geometry().top(); + int width = tabBox->embeddedSize().width(); + int height = tabBox->embeddedSize().height(); + if (alignment.testFlag(Qt::AlignLeft) || alignment.testFlag(Qt::AlignHCenter)) { + x += offset.x(); + } + if (alignment.testFlag(Qt::AlignRight)) { + x = x + info.geometry().width() - offset.x() - width; + } + if (alignment.testFlag(Qt::AlignHCenter)) { + width = info.geometry().width() - 2 * offset.x(); + } + if (alignment.testFlag(Qt::AlignTop) || alignment.testFlag(Qt::AlignVCenter)) { + y += offset.y(); + } + if (alignment.testFlag(Qt::AlignBottom)) { + y = y + info.geometry().height() - offset.y() - height; + } + if (alignment.testFlag(Qt::AlignVCenter)) { + height = info.geometry().height() - 2 * offset.y(); + } + setGeometry(QRect(x, y, width, height)); + } else { + const int width = rootObject()->property("width").toInt(); + const int height = rootObject()->property("height").toInt(); + setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, + m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, + width, height); + } } void DeclarativeView::setCurrentIndex(const QModelIndex &index) @@ -201,9 +252,9 @@ void DeclarativeView::currentIndexChanged(int row) tabBox->setCurrentIndex(m_model->index(row, 0)); } -void DeclarativeView::updateQmlSource() +void DeclarativeView::updateQmlSource(bool force) { - if (tabBox->config().layoutName() == m_currentLayout) { + if (!force && tabBox->config().layoutName() == m_currentLayout) { return; } m_currentLayout = tabBox->config().layoutName(); @@ -215,5 +266,32 @@ void DeclarativeView::updateQmlSource() rootObject()->setProperty("source", QUrl(file)); } +void DeclarativeView::slotEmbeddedChanged(bool enabled) +{ + if (enabled) { + // cache the width + setResizeMode(QDeclarativeView::SizeRootObjectToView); + m_cachedWidth = rootObject()->property("width").toInt(); + m_cachedHeight = rootObject()->property("height").toInt(); + } else { + setResizeMode(QDeclarativeView::SizeViewToRootObject); + if (m_cachedWidth != 0 && m_cachedHeight != 0) { + rootObject()->setProperty("width", m_cachedWidth); + rootObject()->setProperty("height", m_cachedHeight); + } + updateQmlSource(true); + } +} + +void DeclarativeView::slotWindowChanged(WId wId, unsigned int properties) +{ + if (wId != tabBox->embedded()) { + return; + } + if (properties & NET::WMGeometry) { + slotUpdateGeometry(); + } +} + } // namespace TabBox } // namespace KWin diff --git a/tabbox/declarative.h b/tabbox/declarative.h index 56740549e4..341ecb6595 100644 --- a/tabbox/declarative.h +++ b/tabbox/declarative.h @@ -58,11 +58,16 @@ public: void setCurrentIndex(const QModelIndex &index); QModelIndex indexAt(const QPoint &pos) const; +protected: + virtual void hideEvent(QHideEvent *event); + public Q_SLOTS: void slotUpdateGeometry(); + void slotEmbeddedChanged(bool enabled); private Q_SLOTS: - void updateQmlSource(); + void updateQmlSource(bool force = false); void currentIndexChanged(int row); + void slotWindowChanged(WId wId, unsigned int properties); private: QAbstractItemModel *m_model; QRect m_currentScreenGeometry; @@ -71,6 +76,8 @@ private: */ Plasma::FrameSvg* m_frame; QString m_currentLayout; + int m_cachedWidth; + int m_cachedHeight; }; } // namespace TabBox diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 42f007cfc9..a84081015a 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -968,7 +968,23 @@ void TabBox::open(bool modal) } else { m_tabGrab = false; } + m_noModifierGrab = !modal; + setMode(TabBoxWindowsMode); + reset(); + show(); +} + +void TabBox::openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment) +{ + if (isDisplayed()) { + return; + } + m_tabGrab = false; m_noModifierGrab = true; + tabBox->setEmbedded(static_cast(wid)); + tabBox->setEmbeddedOffset(offset); + tabBox->setEmbeddedSize(size); + tabBox->setEmbeddedAlignment(static_cast(horizontalAlignment) | static_cast(verticalAlignment)); setMode(TabBoxWindowsMode); reset(); show(); @@ -980,6 +996,7 @@ bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) return false; m_tabGrab = true; m_noModifierGrab = false; + tabBox->resetEmbedded(); modalActionsSwitch(false); setMode(mode); reset(); diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 60fd4009d2..a0d5a8bce5 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -175,6 +175,22 @@ public slots: * mode or whether the TabBox is controlled externally (e.g. through an effect). **/ Q_SCRIPTABLE void open(bool modal = true); + /** + * Opens the TabBox view embedded on a different window. This implies non-modal mode. + * The geometry of the TabBox is determined by offset, size and the alignment flags. + * If the alignment flags are set to center the view scales with the container. That is if + * the window where the TabBox is embedded onto resizes, the TabBox resizes, too. + * The alignment in combination with the offset determines to what border the TabBox is snapped. + * E.g. if horizontal alignment is right the offset is interpreted as the offset between right + * corner of TabBox view and the container view. When the container changes its geometry this + * offset is kept. So the offset on the left side would increase. + * @param wid The window Id the TabBox should be embedded onto + * @param offset The offset to one of the size borders + * @param size The size of the TabBox. To use the same size as the container, set alignment to center + * @param horizontalAlignment Either Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight + * @param verticalAlignment Either Qt::AlignTop, Qt::AlignVCenter or Qt::AlignBottom + **/ + Q_SCRIPTABLE void openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment); Q_SCRIPTABLE void close(bool abort = false); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index ac05d2670e..da3216ae5d 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -89,11 +89,18 @@ public: bool isShown; QMap< QString, ItemLayoutConfig > tabBoxLayouts; TabBoxClient *lastRaisedClient, *lastRaisedClientSucc; + WId m_embedded; + QPoint m_embeddedOffset; + QSize m_embeddedSize; + Qt::Alignment m_embeddedAlignment; }; TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : view(NULL) , m_declarativeView(NULL) + , m_embedded(0) + , m_embeddedOffset(QPoint(0, 0)) + , m_embeddedSize(QSize(0, 0)) { this->q = q; isShown = false; @@ -698,6 +705,58 @@ QWidget* TabBoxHandler::tabBoxView() const return d->view; } +WId TabBoxHandler::embedded() const +{ + return d->m_embedded; +} + +void TabBoxHandler::setEmbedded(WId wid) +{ + d->m_embedded = wid; + emit embeddedChanged(wid != 0); +} + +void TabBoxHandler::setEmbeddedOffset(const QPoint &offset) +{ + d->m_embeddedOffset = offset; +} + +void TabBoxHandler::setEmbeddedSize(const QSize &size) +{ + d->m_embeddedSize = size; +} + +const QPoint &TabBoxHandler::embeddedOffset() const +{ + return d->m_embeddedOffset; +} + +const QSize &TabBoxHandler::embeddedSize() const +{ + return d->m_embeddedSize; +} + +Qt::Alignment TabBoxHandler::embeddedAlignment() const +{ + return d->m_embeddedAlignment; +} + +void TabBoxHandler::setEmbeddedAlignment(Qt::Alignment alignment) +{ + d->m_embeddedAlignment = alignment; +} + +void TabBoxHandler::resetEmbedded() +{ + if (d->m_embedded == 0) { + return; + } + d->m_embedded = 0; + d->m_embeddedOffset = QPoint(0, 0); + d->m_embeddedSize = QSize(0, 0); + emit embeddedChanged(false); +} + TabBoxHandler* tabBox = 0; TabBoxClient::TabBoxClient() diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index 1355674461..ec15cd5d07 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -315,6 +315,16 @@ public: */ QModelIndex first() const; + void setEmbedded(WId wid); + WId embedded() const; + void setEmbeddedOffset(const QPoint &offset); + const QPoint &embeddedOffset() const; + void setEmbeddedSize(const QSize &size); + const QSize &embeddedSize() const; + void setEmbeddedAlignment(Qt::Alignment alignment); + Qt::Alignment embeddedAlignment() const; + void resetEmbedded(); + /** * @return The tabBoxView Widget */ @@ -348,6 +358,7 @@ signals: */ void configChanged(); void ready(); + void embeddedChanged(bool enabled); private: friend class TabBoxHandlerPrivate; From 946038f3c0ab96bd4f89492cae0f59f2ee462e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 1 Dec 2011 11:06:19 +0100 Subject: [PATCH 10/16] Use isDisplayed instead of isGrabbed to check if TabBox is shown IsDisplayed also works in non-modal mode. IsGrabbed was just legacy. --- workspace.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workspace.cpp b/workspace.cpp index 6a8e2df2ea..8878c0f239 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -638,7 +638,7 @@ void Workspace::addClient(Client* c, allowed_t) updateToolWindows(true); checkNonExistentClients(); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed()) + if (tabBox()->isDisplayed()) tab_box->reset(true); #endif } @@ -672,7 +672,7 @@ void Workspace::removeClient(Client* c, allowed_t) Notify::raise(Notify::Delete); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed() && tabBox()->currentClient() == c) + if (tabBox()->isDisplayed() && tabBox()->currentClient() == c) tab_box->nextPrev(true); #endif @@ -708,7 +708,7 @@ void Workspace::removeClient(Client* c, allowed_t) updateCompositeBlocking(); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed()) + if (tabBox()->isDisplayed()) tab_box->reset(true); #endif From f644c28180d238b7efe3dc2ff4f2ea3559c4081c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 1 Dec 2011 13:15:11 +0100 Subject: [PATCH 11/16] Support for sticky items in TabBox list This is needed for Plasma Active's home screen which should always be the first element in the list. --- atoms.cpp | 3 +++ atoms.h | 1 + client.cpp | 19 +++++++++++++++++++ client.h | 8 ++++++++ events.cpp | 2 ++ manage.cpp | 1 + tabbox/clientmodel.cpp | 11 +++++++++++ tabbox/tabbox.cpp | 5 +++++ tabbox/tabbox.h | 1 + tabbox/tabboxhandler.h | 1 + 10 files changed, 52 insertions(+) diff --git a/atoms.cpp b/atoms.cpp index 1fd237d18b..c2d7f04490 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -126,6 +126,9 @@ Atoms::Atoms() atoms[n] = &kde_net_wm_tab_group; names[n++] = (char*) "_KDE_NET_WM_TAB_GROUP"; + atoms[n] = &kde_first_in_window_list; + names[n++] = (char*) "_KDE_FIRST_IN_WINDOWLIST"; + assert(n <= max); XInternAtoms(display(), names, n, false, atoms_return); diff --git a/atoms.h b/atoms.h index 1f9085a265..c45b0b05a2 100644 --- a/atoms.h +++ b/atoms.h @@ -64,6 +64,7 @@ public: Atom kde_net_wm_shadow; Atom kde_net_wm_opaque_region; Atom kde_net_wm_tab_group; + Atom kde_first_in_window_list; }; diff --git a/client.cpp b/client.cpp index 52d181a1cc..3c249d7347 100644 --- a/client.cpp +++ b/client.cpp @@ -127,6 +127,7 @@ Client::Client(Workspace* ws) , demandAttentionKNotifyTimer(NULL) , m_responsibleForDecoPixmap(false) , paintRedirector(0) + , m_firstInTabBox(false) , electricMaximizing(false) , activitiesDefined(false) , needsSessionInteract(false) @@ -2393,6 +2394,24 @@ QRect Client::decorationRect() const } } +void Client::updateFirstInTabBox() +{ + // TODO: move into KWindowInfo + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + status = XGetWindowProperty(display(), window(), atoms->kde_first_in_window_list, 0, 1, false, atoms->kde_first_in_window_list, &type, &format, &nitems, &extra, &data); + if (status == Success && format == 32 && nitems == 1) { + setFirstInTabBox(true); + } else { + setFirstInTabBox(false); + } + if (data) + XFree(data); +} + } // namespace #include "client.moc" diff --git a/client.h b/client.h index 26c7c74be8..40b4bcd173 100644 --- a/client.h +++ b/client.h @@ -426,6 +426,13 @@ public: TabBox::TabBoxClientImpl* tabBoxClient() const { return m_tabBoxClient; } + bool isFirstInTabBox() const { + return m_firstInTabBox; + } + void setFirstInTabBox(bool enable) { + m_firstInTabBox = enable; + } + void updateFirstInTabBox(); //sets whether the client should be treated as a SessionInteract window void setSessionInteract(bool needed); @@ -718,6 +725,7 @@ private: bool m_responsibleForDecoPixmap; PaintRedirector* paintRedirector; TabBox::TabBoxClientImpl* m_tabBoxClient; + bool m_firstInTabBox; bool electricMaximizing; QuickTileMode electricMode; diff --git a/events.cpp b/events.cpp index fd1147d22a..289e3f7864 100644 --- a/events.cpp +++ b/events.cpp @@ -857,6 +857,8 @@ void Client::propertyNotifyEvent(XPropertyEvent* e) checkActivities(); else if (e->atom == atoms->kde_net_wm_block_compositing) updateCompositeBlocking(true); + else if (e->atom == atoms->kde_first_in_window_list) + updateFirstInTabBox(); break; } } diff --git a/manage.cpp b/manage.cpp index 1877a3d96b..59c7674cbe 100644 --- a/manage.cpp +++ b/manage.cpp @@ -142,6 +142,7 @@ bool Client::manage(Window w, bool isMapped) original_skip_taskbar = skip_taskbar = (info->state() & NET::SkipTaskbar) != 0; skip_pager = (info->state() & NET::SkipPager) != 0; + updateFirstInTabBox(); setupCompositing(); diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index 92c748dbb5..4ca7fef80a 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -175,6 +175,7 @@ void ClientModel::createClientList(int desktop, bool partialReset) start = m_clientList.first(); m_clientList.clear(); + QList stickyClients; switch(tabBox->config().clientSwitchingMode()) { case TabBoxConfig::FocusChainSwitching: { @@ -190,6 +191,9 @@ void ClientModel::createClientList(int desktop, bool partialReset) m_clientList.prepend(add); } else m_clientList += add; + if (add->isFirstInTabBox()) { + stickyClients << add; + } } c = tabBox->nextClientFocusChain(c); @@ -214,6 +218,9 @@ void ClientModel::createClientList(int desktop, bool partialReset) m_clientList.prepend(add); } else m_clientList += add; + if (add->isFirstInTabBox()) { + stickyClients << add; + } } if (index >= stacking.size() - 1) { c = NULL; @@ -227,6 +234,10 @@ void ClientModel::createClientList(int desktop, bool partialReset) break; } } + foreach (TabBoxClient *c, stickyClients) { + m_clientList.removeAll(c); + m_clientList.prepend(c); + } if (tabBox->config().isShowDesktop()) { TabBoxClient* desktopClient = tabBox->desktopClient(); if (desktopClient) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index a84081015a..6f458578c0 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -297,6 +297,11 @@ void TabBoxClientImpl::close() m_client->closeWindow(); } +bool TabBoxClientImpl::isFirstInTabBox() const +{ + return m_client->isFirstInTabBox(); +} + /********************************************************* * TabBox *********************************************************/ diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index a0d5a8bce5..716cf7e0cb 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -83,6 +83,7 @@ public: virtual int height() const; virtual bool isCloseable() const; virtual void close(); + virtual bool isFirstInTabBox() const; Client* client() const { return m_client; diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index ec15cd5d07..00cc3e7590 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -401,6 +401,7 @@ public: virtual int height() const = 0; virtual bool isCloseable() const = 0; virtual void close() = 0; + virtual bool isFirstInTabBox() const = 0; }; /** From 12b7bdfcb1540607aff6dd13d75a917711728971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 1 Dec 2011 13:16:21 +0100 Subject: [PATCH 12/16] Plasma Active needs TabBox now --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d75cf14e78..92b63dc677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ endif(${KDE_PLATFORM_PROFILE} STREQUAL "Desktop") if(KWIN_PLASMA_ACTIVE) set(KWIN_BUILD_DECORATIONS OFF) set(KWIN_BUILD_KCMS OFF) - set(KWIN_BUILD_TABBOX OFF) set(KWIN_BUILD_TILING OFF) set(KWIN_BUILD_DESKTOPCHANGEOSD OFF) set(KWIN_BUILD_SCREENEDGES OFF) From e873efdc994ddce1d0eee6e3d04d6d1f38e24b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 2 Dec 2011 10:31:52 +0100 Subject: [PATCH 13/16] Adding an accept and reject method to TabBox --- tabbox/tabbox.cpp | 52 +++++++++++++++++++---------------------------- tabbox/tabbox.h | 2 ++ 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 6f458578c0..5de486ec39 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -216,21 +216,9 @@ QVector< Window > TabBoxHandlerImpl::outlineWindowIds() const void TabBoxHandlerImpl::activateAndClose() { - Client* c = NULL; - if (TabBoxClientImpl* cl = static_cast< TabBoxClientImpl* >(client(currentIndex()))) { - c = cl->client(); - } - m_tabBox->close(); - if (c) { - Workspace::self()->activateClient(c); - if (c->isShade() && options->shadeHover) - c->setShade(ShadeActivated); - if (c->isDesktop()) - Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); - } + m_tabBox->accept(); } - /********************************************************* * TabBoxClientImpl *********************************************************/ @@ -695,15 +683,7 @@ void TabBox::grabbedKeyEvent(QKeyEvent* event) } if (m_noModifierGrab) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) { - Client* c = currentClient(); - close(); - if (c) { - Workspace::self()->activateClient(c); - if (c->isShade() && options->shadeHover) - c->setShade(ShadeActivated); - if (c->isDesktop()) - Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); - } + accept(); } } setCurrentIndex(m_tabBox->grabbedKeyEvent(event)); @@ -1186,6 +1166,24 @@ void TabBox::close(bool abort) m_noModifierGrab = false; } +void TabBox::accept() +{ + Client* c = currentClient(); + close(); + if (c) { + Workspace::self()->activateClient(c); + if (c->isShade() && options->shadeHover) + c->setShade(ShadeActivated); + if (c->isDesktop()) + Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); + } +} + +void TabBox::reject() +{ + close(true); +} + /*! Handles alt-tab / control-tab releasing */ @@ -1227,16 +1225,8 @@ void TabBox::keyRelease(const XKeyEvent& ev) return; if (m_tabGrab) { bool old_control_grab = m_desktopGrab; - Client* c = currentClient(); - close(); + accept(); m_desktopGrab = old_control_grab; - if (c) { - Workspace::self()->activateClient(c); - if (c->isShade() && options->shadeHover) - c->setShade(ShadeActivated); - if (c->isDesktop()) - Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); - } } if (m_desktopGrab) { bool old_tab_grab = m_tabGrab; diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 716cf7e0cb..184b01c998 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -193,6 +193,8 @@ public slots: **/ Q_SCRIPTABLE void openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment); Q_SCRIPTABLE void close(bool abort = false); + Q_SCRIPTABLE void accept(); + Q_SCRIPTABLE void reject(); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); From ff8c0e3f8f30c78a43620858a7eadb18696696fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 2 Dec 2011 10:48:04 +0100 Subject: [PATCH 14/16] Emit a signal when an item gets selected --- tabbox/tabbox.cpp | 1 + tabbox/tabbox.h | 1 + tabbox/tabboxhandler.cpp | 1 + tabbox/tabboxhandler.h | 1 + 4 files changed, 4 insertions(+) diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 5de486ec39..ff4084cace 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -330,6 +330,7 @@ TabBox::TabBox(QObject *parent) m_desktopListConfig.setLayout(TabBoxConfig::VerticalLayout); m_tabBox = new TabBoxHandlerImpl(this); connect(m_tabBox, SIGNAL(ready()), SLOT(handlerReady())); + connect(m_tabBox, SIGNAL(selectedIndexChanged()), SIGNAL(itemSelected())); m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 184b01c998..6d4e2b2479 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -220,6 +220,7 @@ public slots: signals: void tabBoxAdded(int); Q_SCRIPTABLE void tabBoxClosed(); + Q_SCRIPTABLE void itemSelected(); void tabBoxUpdated(); void tabBoxKeyEvent(QKeyEvent*); diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index da3216ae5d..00c0343f0b 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -559,6 +559,7 @@ void TabBoxHandler::setCurrentIndex(const QModelIndex& index) d->updateHighlightWindows(); } } + emit selectedIndexChanged(); } const QModelIndex& TabBoxHandler::currentIndex() const diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index 00cc3e7590..a20801b2db 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -359,6 +359,7 @@ signals: void configChanged(); void ready(); void embeddedChanged(bool enabled); + void selectedIndexChanged(); private: friend class TabBoxHandlerPrivate; From b770c56eecdee9b5a5f84a3beaf4a3738c039baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 2 Dec 2011 10:48:22 +0100 Subject: [PATCH 15/16] Do not close TabBox from window strip on click --- tabbox/qml/window_strip.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml index 801bc61158..b63a138b90 100644 --- a/tabbox/qml/window_strip.qml +++ b/tabbox/qml/window_strip.qml @@ -82,7 +82,10 @@ Item { anchors.centerIn: parent MouseArea { anchors.fill: parent - onClicked: thumbnailListView.model.activate(index) + onClicked: { + thumbnailListView.currentIndex = index; + thumbnailListView.currentIndexChanged(index); + } } } PlasmaComponents.Label { From 1ccef778f16a688a4cb545e5a1ad76e1c19e0b90 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Thu, 15 Dec 2011 17:47:23 +0100 Subject: [PATCH 16/16] if embedded in a window, forward the X events makes possible to drag the Plasma active panel from the window strip --- tabbox/declarative.cpp | 27 +++++++++++++++++++++++++++ tabbox/declarative.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/tabbox/declarative.cpp b/tabbox/declarative.cpp index 3828f1a3f0..81103cc687 100644 --- a/tabbox/declarative.cpp +++ b/tabbox/declarative.cpp @@ -27,6 +27,8 @@ along with this program. If not, see . #include #include #include +#include + // include KDE #include #include @@ -186,6 +188,28 @@ void DeclarativeView::hideEvent(QHideEvent *event) } } +bool DeclarativeView::x11Event(XEvent *e) +{ + if (tabBox->embedded() && + (e->type == ButtonPress || e->type == ButtonRelease || e->type == MotionNotify)) { + XEvent ev; + + memcpy(&ev, e, sizeof(ev)); + if (e->type == ButtonPress || e->type == ButtonRelease) { + ev.xbutton.x += m_relativePos.x(); + ev.xbutton.y += m_relativePos.y(); + ev.xbutton.window = tabBox->embedded(); + } else if (e->type == MotionNotify) { + ev.xmotion.x += m_relativePos.x(); + ev.xmotion.y += m_relativePos.y(); + ev.xmotion.window = tabBox->embedded(); + } + + XSendEvent( QX11Info::display(), tabBox->embedded(), False, NoEventMask, &ev ); + } + return QDeclarativeView::x11Event(e); +} + void DeclarativeView::slotUpdateGeometry() { const WId embeddedId = tabBox->embedded(); @@ -216,12 +240,15 @@ void DeclarativeView::slotUpdateGeometry() height = info.geometry().height() - 2 * offset.y(); } setGeometry(QRect(x, y, width, height)); + + m_relativePos = QPoint(info.geometry().x(), info.geometry().x()); } else { const int width = rootObject()->property("width").toInt(); const int height = rootObject()->property("height").toInt(); setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, width, height); + m_relativePos = pos(); } } diff --git a/tabbox/declarative.h b/tabbox/declarative.h index 341ecb6595..104e6cf79d 100644 --- a/tabbox/declarative.h +++ b/tabbox/declarative.h @@ -60,6 +60,7 @@ public: protected: virtual void hideEvent(QHideEvent *event); + virtual bool x11Event(XEvent *e); public Q_SLOTS: void slotUpdateGeometry(); @@ -78,6 +79,8 @@ private: QString m_currentLayout; int m_cachedWidth; int m_cachedHeight; + //relative position to the embedding window + QPoint m_relativePos; }; } // namespace TabBox