diff --git a/autotests/tabbox/mock_tabboxhandler.h b/autotests/tabbox/mock_tabboxhandler.h index b76a9f86b..4820a3d6a 100644 --- a/autotests/tabbox/mock_tabboxhandler.h +++ b/autotests/tabbox/mock_tabboxhandler.h @@ -1,109 +1,113 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Martin Gräßlin 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 . *********************************************************************/ #ifndef KWIN_MOCK_TABBOX_HANDLER_H #define KWIN_MOCK_TABBOX_HANDLER_H #include "../../tabbox/tabboxhandler.h" namespace KWin { class MockTabBoxHandler : public TabBox::TabBoxHandler { Q_OBJECT public: MockTabBoxHandler(QObject *parent = nullptr); virtual ~MockTabBoxHandler(); virtual void activateAndClose() { } virtual QWeakPointer< TabBox::TabBoxClient > activeClient() const; void setActiveClient(const QWeakPointer &client); virtual int activeScreen() const { return 0; } virtual QWeakPointer< TabBox::TabBoxClient > clientToAddToList(TabBox::TabBoxClient *client, int desktop) const; virtual int currentDesktop() const { return 1; } virtual QWeakPointer< TabBox::TabBoxClient > desktopClient() const { return QWeakPointer(); } virtual QString desktopName(int desktop) const { Q_UNUSED(desktop) return "desktop 1"; } virtual QString desktopName(TabBox::TabBoxClient *client) const { Q_UNUSED(client) return "desktop"; } virtual void elevateClient(TabBox::TabBoxClient *c, QWindow *tabbox, bool elevate) const { Q_UNUSED(c) Q_UNUSED(tabbox) Q_UNUSED(elevate) } virtual void shadeClient(TabBox::TabBoxClient *c, bool b) const { Q_UNUSED(c) Q_UNUSED(b) } virtual void hideOutline() { } virtual QWeakPointer< TabBox::TabBoxClient > nextClientFocusChain(TabBox::TabBoxClient *client) const; virtual QWeakPointer firstClientFocusChain() const; virtual bool isInFocusChain (TabBox::TabBoxClient* client) const; virtual int nextDesktopFocusChain(int desktop) const { Q_UNUSED(desktop) return 1; } virtual int numberOfDesktops() const { return 1; } virtual QVector< xcb_window_t > outlineWindowIds() const { return QVector(); } virtual bool isKWinCompositing() const { return false; } virtual void raiseClient(TabBox::TabBoxClient *c) const { Q_UNUSED(c) } virtual void restack(TabBox::TabBoxClient *c, TabBox::TabBoxClient *under) { Q_UNUSED(c) Q_UNUSED(under) } virtual void showOutline(const QRect &outline) { Q_UNUSED(outline) } virtual TabBox::TabBoxClientList stackingOrder() const { return TabBox::TabBoxClientList(); } virtual void grabbedKeyEvent(QKeyEvent *event) const; void highlightWindows(TabBox::TabBoxClient *window = nullptr, QWindow *controller = nullptr) override { Q_UNUSED(window) Q_UNUSED(controller) } + bool noModifierGrab() const override { + return false; + } + // mock methods QWeakPointer createMockWindow(const QString &caption, WId id); void closeWindow(TabBox::TabBoxClient *client); private: QList< QSharedPointer > m_windows; QWeakPointer m_activeClient; }; } // namespace KWin #endif diff --git a/tabbox/switcheritem.cpp b/tabbox/switcheritem.cpp index 47a33e5cf..cce433672 100644 --- a/tabbox/switcheritem.cpp +++ b/tabbox/switcheritem.cpp @@ -1,106 +1,115 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin 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 . *********************************************************************/ #include "switcheritem.h" // KWin #include "tabboxhandler.h" #include "screens.h" // Qt #include namespace KWin { namespace TabBox { SwitcherItem::SwitcherItem(QObject *parent) : QObject(parent) , m_model(nullptr) , m_item(nullptr) , m_visible(false) , m_allDesktops(false) , m_currentIndex(0) { m_selectedIndexConnection = connect(tabBox, &TabBoxHandler::selectedIndexChanged, [this] { if (isVisible()) { setCurrentIndex(tabBox->currentIndex().row()); } }); connect(screens(), &Screens::changed, this, &SwitcherItem::screenGeometryChanged); } SwitcherItem::~SwitcherItem() { disconnect(m_selectedIndexConnection); } void SwitcherItem::setItem(QObject *item) { if (m_item == item) { return; } m_item = item; emit itemChanged(); } void SwitcherItem::setModel(QAbstractItemModel *model) { m_model = model; emit modelChanged(); } void SwitcherItem::setVisible(bool visible) { if (m_visible == visible) { return; } if (visible) emit screenGeometryChanged(); m_visible = visible; emit visibleChanged(); } QRect SwitcherItem::screenGeometry() const { return screens()->geometry(screens()->current()); } void SwitcherItem::setCurrentIndex(int index) { if (m_currentIndex == index) { return; } m_currentIndex = index; if (m_model) { tabBox->setCurrentIndex(m_model->index(index, 0)); } emit currentIndexChanged(m_currentIndex); } void SwitcherItem::setAllDesktops(bool all) { if (m_allDesktops == all) { return; } m_allDesktops = all; emit allDesktopsChanged(); } +void SwitcherItem::setNoModifierGrab(bool set) +{ + if (m_noModifierGrab == set) { + return; + } + m_noModifierGrab = set; + emit noModifierGrabChanged(); +} + } } diff --git a/tabbox/switcheritem.h b/tabbox/switcheritem.h index a04cb1bdc..4464a7f97 100644 --- a/tabbox/switcheritem.h +++ b/tabbox/switcheritem.h @@ -1,111 +1,118 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin 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 . *********************************************************************/ #ifndef KWIN_TABBOX_ITEM #define KWIN_TABBOX_ITEM #include #include class QAbstractItemModel; namespace KWin { namespace TabBox { class SwitcherItem : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged) Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged) Q_PROPERTY(bool allDesktops READ isAllDesktops NOTIFY allDesktopsChanged) Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(bool noModifierGrab READ noModifierGrab NOTIFY noModifierGrabChanged) /** * The main QML item that will be displayed in the Dialog */ Q_PROPERTY(QObject *item READ item WRITE setItem NOTIFY itemChanged) Q_CLASSINFO("DefaultProperty", "item") public: SwitcherItem(QObject *parent = nullptr); virtual ~SwitcherItem(); QAbstractItemModel *model() const; QRect screenGeometry() const; bool isVisible() const; bool isAllDesktops() const; int currentIndex() const; void setCurrentIndex(int index); QObject *item() const; void setItem(QObject *item); + bool noModifierGrab() const { + return m_noModifierGrab; + } // for usage from outside void setModel(QAbstractItemModel *model); void setAllDesktops(bool all); void setVisible(bool visible); + void setNoModifierGrab(bool set); Q_SIGNALS: void visibleChanged(); void currentIndexChanged(int index); void modelChanged(); void allDesktopsChanged(); void screenGeometryChanged(); void itemChanged(); + void noModifierGrabChanged(); private: QAbstractItemModel *m_model; QObject *m_item; bool m_visible; bool m_allDesktops; int m_currentIndex; QMetaObject::Connection m_selectedIndexConnection; + bool m_noModifierGrab = false; }; inline QAbstractItemModel *SwitcherItem::model() const { return m_model; } inline bool SwitcherItem::isVisible() const { return m_visible; } inline bool SwitcherItem::isAllDesktops() const { return m_allDesktops; } inline int SwitcherItem::currentIndex() const { return m_currentIndex; } inline QObject *SwitcherItem::item() const { return m_item; } } // TabBox } // KWin #endif // KWIN_TABBOX_ITEM diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 014c09b7b..4dc2a599a 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -1,1705 +1,1710 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 Martin Gräßlin 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 . *********************************************************************/ //#define QT_CLEAN_NAMESPACE // own #include "tabbox.h" // tabbox #include "tabbox/clientmodel.h" #include "tabbox/desktopmodel.h" #include "tabbox/tabboxconfig.h" #include "tabbox/desktopchain.h" #include "tabbox/tabbox_logging.h" // kwin #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif #include "client.h" #include "effects.h" #include "input.h" #include "keyboard_input.h" #include "focuschain.h" #include "screenedge.h" #include "screens.h" #include "unmanaged.h" #include "virtualdesktops.h" #include "workspace.h" #include "xcbutils.h" // Qt #include #include // KDE #include #include #include #include #include // X11 #include #include // xcb #include // specify externals before namespace namespace KWin { namespace TabBox { TabBoxHandlerImpl::TabBoxHandlerImpl(TabBox* tabBox) : TabBoxHandler(tabBox) , m_tabBox(tabBox) , m_desktopFocusChain(new DesktopChainManager(this)) { // connects for DesktopFocusChainManager VirtualDesktopManager *vds = VirtualDesktopManager::self(); connect(vds, SIGNAL(countChanged(uint,uint)), m_desktopFocusChain, SLOT(resize(uint,uint))); connect(vds, SIGNAL(currentChanged(uint,uint)), m_desktopFocusChain, SLOT(addDesktop(uint,uint))); #ifdef KWIN_BUILD_ACTIVITIES if (Activities::self()) { connect(Activities::self(), SIGNAL(currentChanged(QString)), m_desktopFocusChain, SLOT(useChain(QString))); } #endif } TabBoxHandlerImpl::~TabBoxHandlerImpl() { } int TabBoxHandlerImpl::activeScreen() const { return screens()->current(); } int TabBoxHandlerImpl::currentDesktop() const { return VirtualDesktopManager::self()->current(); } QString TabBoxHandlerImpl::desktopName(TabBoxClient* client) const { if (TabBoxClientImpl* c = static_cast< TabBoxClientImpl* >(client)) { if (!c->client()->isOnAllDesktops()) return VirtualDesktopManager::self()->name(c->client()->desktop()); } return VirtualDesktopManager::self()->name(VirtualDesktopManager::self()->current()); } QString TabBoxHandlerImpl::desktopName(int desktop) const { return VirtualDesktopManager::self()->name(desktop); } QWeakPointer TabBoxHandlerImpl::nextClientFocusChain(TabBoxClient* client) const { if (TabBoxClientImpl* c = static_cast< TabBoxClientImpl* >(client)) { auto next = FocusChain::self()->nextMostRecentlyUsed(c->client()); if (next) return next->tabBoxClient(); } return QWeakPointer(); } QWeakPointer< TabBoxClient > TabBoxHandlerImpl::firstClientFocusChain() const { if (auto c = FocusChain::self()->firstMostRecentlyUsed()) { return QWeakPointer(c->tabBoxClient()); } else { return QWeakPointer(); } } bool TabBoxHandlerImpl::isInFocusChain(TabBoxClient *client) const { if (TabBoxClientImpl *c = static_cast(client)) { return FocusChain::self()->contains(c->client()); } return false; } int TabBoxHandlerImpl::nextDesktopFocusChain(int desktop) const { return m_desktopFocusChain->next(desktop); } int TabBoxHandlerImpl::numberOfDesktops() const { return VirtualDesktopManager::self()->count(); } QWeakPointer TabBoxHandlerImpl::activeClient() const { if (Workspace::self()->activeClient()) return Workspace::self()->activeClient()->tabBoxClient(); else return QWeakPointer(); } bool TabBoxHandlerImpl::checkDesktop(TabBoxClient* client, int desktop) const { auto current = (static_cast< TabBoxClientImpl* >(client))->client(); switch (config().clientDesktopMode()) { case TabBoxConfig::AllDesktopsClients: return true; case TabBoxConfig::ExcludeCurrentDesktopClients: return !current->isOnDesktop(desktop); default: // TabBoxConfig::OnlyCurrentDesktopClients return current->isOnDesktop(desktop); } } bool TabBoxHandlerImpl::checkActivity(TabBoxClient* client) const { auto current = (static_cast< TabBoxClientImpl* >(client))->client(); switch (config().clientActivitiesMode()) { case TabBoxConfig::AllActivitiesClients: return true; case TabBoxConfig::ExcludeCurrentActivityClients: return !current->isOnCurrentActivity(); default: // TabBoxConfig::OnlyCurrentActivityClients return current->isOnCurrentActivity(); } } bool TabBoxHandlerImpl::checkApplications(TabBoxClient* client) const { auto current = (static_cast< TabBoxClientImpl* >(client))->client(); TabBoxClientImpl* c; QListIterator< QWeakPointer > i(clientList()); switch (config().clientApplicationsMode()) { case TabBoxConfig::OneWindowPerApplication: // check if the list already contains an entry of this application while (i.hasNext()) { QSharedPointer client = i.next().toStrongRef(); if (!client) { continue; } if ((c = dynamic_cast< TabBoxClientImpl* >(client.data()))) { if (c->client()->resourceClass() == current->resourceClass()) { return false; } } } return true; case TabBoxConfig::AllWindowsCurrentApplication: { QSharedPointer pointer = tabBox->activeClient().toStrongRef(); if (!pointer) { return false; } if ((c = dynamic_cast< TabBoxClientImpl* >(pointer.data()))) { if (c->client()->resourceClass() == current->resourceClass()) { return true; } } return false; } default: // TabBoxConfig::AllWindowsAllApplications return true; } } bool TabBoxHandlerImpl::checkMinimized(TabBoxClient* client) const { switch (config().clientMinimizedMode()) { case TabBoxConfig::ExcludeMinimizedClients: return !client->isMinimized(); case TabBoxConfig::OnlyMinimizedClients: return client->isMinimized(); default: // TabBoxConfig::IgnoreMinimizedStatus return true; } } bool TabBoxHandlerImpl::checkMultiScreen(TabBoxClient* client) const { auto current = (static_cast< TabBoxClientImpl* >(client))->client(); switch (config().clientMultiScreenMode()) { case TabBoxConfig::IgnoreMultiScreen: return true; case TabBoxConfig::ExcludeCurrentScreenClients: return current->screen() != screens()->current(); default: // TabBoxConfig::OnlyCurrentScreenClients return current->screen() == screens()->current(); } } QWeakPointer TabBoxHandlerImpl::clientToAddToList(TabBoxClient* client, int desktop) const { if (!client) { return QWeakPointer(); } AbstractClient* ret = nullptr; AbstractClient* current = (static_cast< TabBoxClientImpl* >(client))->client(); bool addClient = checkDesktop(client, desktop) && checkActivity(client) && checkApplications(client) && checkMinimized(client) && checkMultiScreen(client); addClient = addClient && current->wantsTabFocus() && !current->skipSwitcher(); if (addClient) { // don't add windows that have modal dialogs AbstractClient* modal = current->findModal(); if (modal == nullptr || modal == current) ret = current; else if (!clientList().contains(modal->tabBoxClient())) ret = modal; else { // nothing } } if (ret) return ret->tabBoxClient(); else return QWeakPointer(); } TabBoxClientList TabBoxHandlerImpl::stackingOrder() const { ToplevelList stacking = Workspace::self()->stackingOrder(); TabBoxClientList ret; foreach (Toplevel *toplevel, stacking) { if (Client *client = qobject_cast(toplevel)) { ret.append(client->tabBoxClient()); } } return ret; } bool TabBoxHandlerImpl::isKWinCompositing() const { return Workspace::self()->compositing(); } void TabBoxHandlerImpl::raiseClient(TabBoxClient* c) const { Workspace::self()->raiseClient(static_cast(c)->client()); } void TabBoxHandlerImpl::restack(TabBoxClient *c, TabBoxClient *under) { Workspace::self()->restack(static_cast(c)->client(), static_cast(under)->client(), true); } void TabBoxHandlerImpl::elevateClient(TabBoxClient *c, QWindow *tabbox, bool b) const { auto cl = static_cast(c)->client(); cl->elevate(b); if (Toplevel *w = Workspace::self()->findInternal(tabbox)) w->elevate(b); } void TabBoxHandlerImpl::shadeClient(TabBoxClient *c, bool b) const { Client *cl = dynamic_cast(static_cast(c)->client()); if (!cl) { // shading is X11 specific return; } cl->cancelShadeHoverTimer(); // stop core shading action if (!b && cl->shadeMode() == ShadeNormal) cl->setShade(ShadeHover); else if (b && cl->shadeMode() == ShadeHover) cl->setShade(ShadeNormal); } QWeakPointer TabBoxHandlerImpl::desktopClient() const { foreach (Toplevel *toplevel, Workspace::self()->stackingOrder()) { Client *client = qobject_cast(toplevel); if (client && client->isDesktop() && client->isOnCurrentDesktop() && client->screen() == screens()->current()) { return client->tabBoxClient(); } } return QWeakPointer(); } void TabBoxHandlerImpl::activateAndClose() { m_tabBox->accept(); } void TabBoxHandlerImpl::highlightWindows(TabBoxClient *window, QWindow *controller) { if (!effects) { return; } QVector windows; if (window) { windows << static_cast(window)->client()->effectWindow(); } if (auto t = Workspace::self()->findToplevel(controller)) { windows << t->effectWindow(); } static_cast(effects)->highlightWindows(windows); } +bool TabBoxHandlerImpl::noModifierGrab() const +{ + return m_tabBox->noModifierGrab(); +} + /********************************************************* * TabBoxClientImpl *********************************************************/ TabBoxClientImpl::TabBoxClientImpl(AbstractClient *client) : TabBoxClient() , m_client(client) { } TabBoxClientImpl::~TabBoxClientImpl() { } QString TabBoxClientImpl::caption() const { if (m_client->isDesktop()) return i18nc("Special entry in alt+tab list for minimizing all windows", "Show Desktop"); return m_client->caption(); } QIcon TabBoxClientImpl::icon() const { if (m_client->isDesktop()) { return QIcon::fromTheme(QStringLiteral("user-desktop")); } return m_client->icon(); } WId TabBoxClientImpl::window() const { return m_client->windowId(); } bool TabBoxClientImpl::isMinimized() const { return m_client->isMinimized(); } int TabBoxClientImpl::x() const { return m_client->x(); } int TabBoxClientImpl::y() const { return m_client->y(); } int TabBoxClientImpl::width() const { return m_client->width(); } int TabBoxClientImpl::height() const { return m_client->height(); } bool TabBoxClientImpl::isCloseable() const { return m_client->isCloseable(); } void TabBoxClientImpl::close() { m_client->closeWindow(); } bool TabBoxClientImpl::isFirstInTabBox() const { return m_client->isFirstInTabBox(); } /********************************************************* * TabBox *********************************************************/ TabBox *TabBox::s_self = nullptr; TabBox *TabBox::create(QObject *parent) { Q_ASSERT(!s_self); s_self = new TabBox(parent); return s_self; } TabBox::TabBox(QObject *parent) : QObject(parent) , m_displayRefcount(0) , m_desktopGrab(false) , m_tabGrab(false) , m_noModifierGrab(false) , m_forcedGlobalMouseGrab(false) , m_ready(false) { m_isShown = false; m_defaultConfig = TabBoxConfig(); m_defaultConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); m_defaultConfig.setClientDesktopMode(TabBoxConfig::OnlyCurrentDesktopClients); m_defaultConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); m_defaultConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); m_defaultConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); m_defaultConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_defaultConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); m_defaultConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); m_alternativeConfig = TabBoxConfig(); m_alternativeConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); m_alternativeConfig.setClientDesktopMode(TabBoxConfig::AllDesktopsClients); m_alternativeConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); m_alternativeConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); m_alternativeConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); m_alternativeConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_alternativeConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); m_alternativeConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); m_defaultCurrentApplicationConfig = m_defaultConfig; m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); m_alternativeCurrentApplicationConfig = m_alternativeConfig; m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); m_desktopConfig = TabBoxConfig(); m_desktopConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); m_desktopConfig.setShowTabBox(true); m_desktopConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_desktopConfig.setDesktopSwitchingMode(TabBoxConfig::MostRecentlyUsedDesktopSwitching); m_desktopListConfig = TabBoxConfig(); m_desktopListConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); m_desktopListConfig.setShowTabBox(true); m_desktopListConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); m_desktopListConfig.setDesktopSwitchingMode(TabBoxConfig::StaticDesktopSwitching); m_tabBox = new TabBoxHandlerImpl(this); QTimer::singleShot(0, this, SLOT(handlerReady())); m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); connect(Workspace::self(), SIGNAL(configChanged()), this, SLOT(reconfigure())); } TabBox::~TabBox() { s_self = nullptr; } void TabBox::handlerReady() { m_tabBox->setConfig(m_defaultConfig); reconfigure(); m_ready = true; } template void TabBox::key(const char *actionName, Slot slot, const QKeySequence &shortcut) { QAction *a = new QAction(this); a->setProperty("componentName", QStringLiteral(KWIN_NAME)); a->setObjectName(QString::fromUtf8(actionName)); a->setText(i18n(actionName)); KGlobalAccel::self()->setShortcut(a, QList() << shortcut); input()->registerShortcut(shortcut, a, TabBox::self(), slot); auto cuts = KGlobalAccel::self()->shortcut(a); globalShortcutChanged(a, cuts.isEmpty() ? QKeySequence() : cuts.first()); } static const char s_windows[] = I18N_NOOP("Walk Through Windows"); static const char s_windowsRev[] = I18N_NOOP("Walk Through Windows (Reverse)"); static const char s_windowsAlt[] = I18N_NOOP("Walk Through Windows Alternative"); static const char s_windowsAltRev[] = I18N_NOOP("Walk Through Windows Alternative (Reverse)"); static const char s_app[] = I18N_NOOP("Walk Through Windows of Current Application"); static const char s_appRev[] = I18N_NOOP("Walk Through Windows of Current Application (Reverse)"); static const char s_appAlt[] = I18N_NOOP("Walk Through Windows of Current Application Alternative"); static const char s_appAltRev[] = I18N_NOOP("Walk Through Windows of Current Application Alternative (Reverse)"); static const char s_desktops[] = I18N_NOOP("Walk Through Desktops"); static const char s_desktopsRev[] = I18N_NOOP("Walk Through Desktops (Reverse)"); static const char s_desktopList[] = I18N_NOOP("Walk Through Desktop List"); static const char s_desktopListRev[] = I18N_NOOP("Walk Through Desktop List (Reverse)"); void TabBox::initShortcuts() { key(s_windows, &TabBox::slotWalkThroughWindows, Qt::ALT + Qt::Key_Tab); key(s_windowsRev, &TabBox::slotWalkBackThroughWindows, Qt::ALT + Qt::SHIFT + Qt::Key_Backtab); key(s_app, &TabBox::slotWalkThroughCurrentAppWindows, Qt::ALT + Qt::Key_QuoteLeft); key(s_appRev, &TabBox::slotWalkBackThroughCurrentAppWindows, Qt::ALT + Qt::Key_AsciiTilde); key(s_windowsAlt, &TabBox::slotWalkThroughWindowsAlternative); key(s_windowsAltRev, &TabBox::slotWalkBackThroughWindowsAlternative); key(s_appAlt, &TabBox::slotWalkThroughCurrentAppWindowsAlternative); key(s_appAltRev, &TabBox::slotWalkBackThroughCurrentAppWindowsAlternative); key(s_desktops, &TabBox::slotWalkThroughDesktops); key(s_desktopsRev, &TabBox::slotWalkBackThroughDesktops); key(s_desktopList, &TabBox::slotWalkThroughDesktopList); key(s_desktopListRev, &TabBox::slotWalkBackThroughDesktopList); connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &TabBox::globalShortcutChanged); } void TabBox::globalShortcutChanged(QAction *action, const QKeySequence &seq) { if (qstrcmp(qPrintable(action->objectName()), s_windows) == 0) { m_cutWalkThroughWindows = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_windowsRev) == 0) { m_cutWalkThroughWindowsReverse = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_app) == 0) { m_cutWalkThroughCurrentAppWindows = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_appRev) == 0) { m_cutWalkThroughCurrentAppWindowsReverse = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAlt) == 0) { m_cutWalkThroughWindowsAlternative = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAltRev) == 0) { m_cutWalkThroughWindowsAlternativeReverse = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_appAlt) == 0) { m_cutWalkThroughCurrentAppWindowsAlternative = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_appAltRev) == 0) { m_cutWalkThroughCurrentAppWindowsAlternativeReverse = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_desktops) == 0) { m_cutWalkThroughDesktops = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_desktopsRev) == 0) { m_cutWalkThroughDesktopsReverse = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_desktopList) == 0) { m_cutWalkThroughDesktopList = seq; } else if (qstrcmp(qPrintable(action->objectName()), s_desktopListRev) == 0) { m_cutWalkThroughDesktopListReverse = seq; } } /*! Sets the current mode to \a mode, either TabBoxDesktopListMode or TabBoxWindowsMode \sa mode() */ void TabBox::setMode(TabBoxMode mode) { m_tabBoxMode = mode; switch(mode) { case TabBoxWindowsMode: m_tabBox->setConfig(m_defaultConfig); break; case TabBoxWindowsAlternativeMode: m_tabBox->setConfig(m_alternativeConfig); break; case TabBoxCurrentAppWindowsMode: m_tabBox->setConfig(m_defaultCurrentApplicationConfig); break; case TabBoxCurrentAppWindowsAlternativeMode: m_tabBox->setConfig(m_alternativeCurrentApplicationConfig); break; case TabBoxDesktopMode: m_tabBox->setConfig(m_desktopConfig); break; case TabBoxDesktopListMode: m_tabBox->setConfig(m_desktopListConfig); break; } } /*! Resets the tab box to display the active client in TabBoxWindowsMode, or the current desktop in TabBoxDesktopListMode */ void TabBox::reset(bool partial_reset) { switch(m_tabBox->config().tabBoxMode()) { case TabBoxConfig::ClientTabBox: m_tabBox->createModel(partial_reset); if (!partial_reset) { if (Workspace::self()->activeClient()) setCurrentClient(Workspace::self()->activeClient()); // it's possible that the active client is not part of the model // in that case the index is invalid if (!m_tabBox->currentIndex().isValid()) setCurrentIndex(m_tabBox->first()); } else { if (!m_tabBox->currentIndex().isValid() || !m_tabBox->client(m_tabBox->currentIndex())) setCurrentIndex(m_tabBox->first()); } break; case TabBoxConfig::DesktopTabBox: m_tabBox->createModel(); if (!partial_reset) setCurrentDesktop(VirtualDesktopManager::self()->current()); break; } emit tabBoxUpdated(); } /*! Shows the next or previous item, depending on \a next */ void TabBox::nextPrev(bool next) { setCurrentIndex(m_tabBox->nextPrev(next), false); emit tabBoxUpdated(); } /*! Returns the currently displayed client ( only works in TabBoxWindowsMode ). Returns 0 if no client is displayed. */ AbstractClient* TabBox::currentClient() { if (TabBoxClientImpl* client = static_cast< TabBoxClientImpl* >(m_tabBox->client(m_tabBox->currentIndex()))) { if (!Workspace::self()->hasClient(client->client())) return nullptr; return client->client(); } else return nullptr; } /*! Returns the list of clients potentially displayed ( only works in TabBoxWindowsMode ). Returns an empty list if no clients are available. */ QList TabBox::currentClientList() { TabBoxClientList list = m_tabBox->clientList(); QList ret; foreach (const QWeakPointer &clientPointer, list) { QSharedPointer client = clientPointer.toStrongRef(); if (!client) continue; if (const TabBoxClientImpl* c = static_cast< const TabBoxClientImpl* >(client.data())) ret.append(c->client()); } return ret; } /*! Returns the currently displayed virtual desktop ( only works in TabBoxDesktopListMode ) Returns -1 if no desktop is displayed. */ int TabBox::currentDesktop() { return m_tabBox->desktop(m_tabBox->currentIndex()); } /*! Returns the list of desktops potentially displayed ( only works in TabBoxDesktopListMode ) Returns an empty list if no are available. */ QList< int > TabBox::currentDesktopList() { return m_tabBox->desktopList(); } /*! Change the currently selected client, and notify the effects. \sa setCurrentDesktop() */ void TabBox::setCurrentClient(AbstractClient *newClient) { setCurrentIndex(m_tabBox->index(newClient->tabBoxClient())); } /*! Change the currently selected desktop, and notify the effects. \sa setCurrentClient() */ void TabBox::setCurrentDesktop(int newDesktop) { setCurrentIndex(m_tabBox->desktopIndex(newDesktop)); } void TabBox::setCurrentIndex(QModelIndex index, bool notifyEffects) { if (!index.isValid()) return; m_tabBox->setCurrentIndex(index); if (notifyEffects) { emit tabBoxUpdated(); } } /*! Notify effects that the tab box is being shown, and only display the default tab box QFrame if no effect has referenced the tab box. */ void TabBox::show() { emit tabBoxAdded(m_tabBoxMode); if (isDisplayed()) { m_isShown = false; return; } workspace()->setShowingDesktop(false); reference(); m_isShown = true; m_tabBox->show(); } /*! Notify effects that the tab box is being hidden. */ void TabBox::hide(bool abort) { m_delayedShowTimer.stop(); if (m_isShown) { m_isShown = false; unreference(); } emit tabBoxClosed(); if (isDisplayed()) qCDebug(KWIN_TABBOX) << "Tab box was not properly closed by an effect"; m_tabBox->hide(abort); Xcb::sync(); } void TabBox::reconfigure() { KSharedConfigPtr c = kwinApp()->config(); KConfigGroup config = c->group("TabBox"); loadConfig(c->group("TabBox"), m_defaultConfig); loadConfig(c->group("TabBoxAlternative"), m_alternativeConfig); m_defaultCurrentApplicationConfig = m_defaultConfig; m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); m_alternativeCurrentApplicationConfig = m_alternativeConfig; m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); m_tabBox->setConfig(m_defaultConfig); m_delayShow = config.readEntry("ShowDelay", true); m_delayShowTime = config.readEntry("DelayTime", 90); const QString defaultDesktopLayout = QStringLiteral("org.kde.breeze.desktop"); m_desktopConfig.setLayoutName(config.readEntry("DesktopLayout", defaultDesktopLayout)); m_desktopListConfig.setLayoutName(config.readEntry("DesktopListLayout", defaultDesktopLayout)); QList *borders = &m_borderActivate; QString borderConfig = QStringLiteral("BorderActivate"); for (int i = 0; i < 2; ++i) { foreach (ElectricBorder border, *borders) { ScreenEdges::self()->unreserve(border, this); } borders->clear(); QStringList list = config.readEntry(borderConfig, QStringList()); foreach (const QString &s, list) { bool ok; const int i = s.toInt(&ok); if (!ok) continue; borders->append(ElectricBorder(i)); ScreenEdges::self()->reserve(ElectricBorder(i), this, "toggle"); } borders = &m_borderAlternativeActivate; borderConfig = QStringLiteral("BorderAlternativeActivate"); } auto touchConfig = [this, config] (const QString &key, QHash &actions, TabBoxMode mode) { // fist erase old config for (auto it = actions.begin(); it != actions.end(); ) { delete it.value(); it = actions.erase(it); } // now new config const QStringList list = config.readEntry(key, QStringList()); for (const auto &s : list) { bool ok; const int i = s.toInt(&ok); if (!ok) { continue; } QAction *a = new QAction(this); connect(a, &QAction::triggered, this, std::bind(&TabBox::toggleMode, this, mode)); ScreenEdges::self()->reserveTouch(ElectricBorder(i), a); actions.insert(ElectricBorder(i), a); } }; touchConfig(QStringLiteral("TouchBorderActivate"), m_touchActivate, TabBoxWindowsMode); touchConfig(QStringLiteral("TouchBorderAlternativeActivate"), m_touchAlternativeActivate, TabBoxWindowsAlternativeMode); } void TabBox::loadConfig(const KConfigGroup& config, TabBoxConfig& tabBoxConfig) { tabBoxConfig.setClientDesktopMode(TabBoxConfig::ClientDesktopMode( config.readEntry("DesktopMode", TabBoxConfig::defaultDesktopMode()))); tabBoxConfig.setClientActivitiesMode(TabBoxConfig::ClientActivitiesMode( config.readEntry("ActivitiesMode", TabBoxConfig::defaultActivitiesMode()))); tabBoxConfig.setClientApplicationsMode(TabBoxConfig::ClientApplicationsMode( config.readEntry("ApplicationsMode", TabBoxConfig::defaultApplicationsMode()))); tabBoxConfig.setClientMinimizedMode(TabBoxConfig::ClientMinimizedMode( config.readEntry("MinimizedMode", TabBoxConfig::defaultMinimizedMode()))); tabBoxConfig.setShowDesktopMode(TabBoxConfig::ShowDesktopMode( config.readEntry("ShowDesktopMode", TabBoxConfig::defaultShowDesktopMode()))); tabBoxConfig.setClientMultiScreenMode(TabBoxConfig::ClientMultiScreenMode( config.readEntry("MultiScreenMode", TabBoxConfig::defaultMultiScreenMode()))); tabBoxConfig.setClientSwitchingMode(TabBoxConfig::ClientSwitchingMode( config.readEntry("SwitchingMode", TabBoxConfig::defaultSwitchingMode()))); tabBoxConfig.setShowTabBox(config.readEntry("ShowTabBox", TabBoxConfig::defaultShowTabBox())); tabBoxConfig.setHighlightWindows(config.readEntry("HighlightWindows", TabBoxConfig::defaultHighlightWindow())); tabBoxConfig.setLayoutName(config.readEntry("LayoutName", TabBoxConfig::defaultLayoutName())); } /*! Rikkus: please document! (Matthias) Ok, here's the docs :) You call delayedShow() instead of show() directly. If the 'ShowDelay' setting is false, show() is simply called. Otherwise, we start a timer for the delay given in the settings and only do a show() when it times out. This means that you can alt-tab between windows and you don't see the tab box immediately. Not only does this make alt-tabbing faster, it gives less 'flicker' to the eyes. You don't need to see the tab box if you're just quickly switching between 2 or 3 windows. It seems to work quite nicely. */ void TabBox::delayedShow() { if (isDisplayed() || m_delayedShowTimer.isActive()) // already called show - no need to call it twice return; if (!m_delayShowTime) { show(); return; } m_delayedShowTimer.setSingleShot(true); m_delayedShowTimer.start(m_delayShowTime); } bool TabBox::handleMouseEvent(xcb_button_press_event_t *e) { xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects if (effects && static_cast(effects)->checkInputWindowEvent(e)) return true; } if ((e->response_type & ~0x80) == XCB_BUTTON_PRESS) { // press outside Tabbox? QPoint pos(e->root_x, e->root_y); if ((!m_isShown && isDisplayed()) || (!m_tabBox->containsPos(pos) && (e->detail == XCB_BUTTON_INDEX_1 || e->detail == XCB_BUTTON_INDEX_2 || e->detail == XCB_BUTTON_INDEX_3))) { close(); // click outside closes tab return true; } if (e->detail == XCB_BUTTON_INDEX_5 || e->detail == XCB_BUTTON_INDEX_4) { // mouse wheel event const QModelIndex index = m_tabBox->nextPrev(e->detail == XCB_BUTTON_INDEX_5); if (index.isValid()) { setCurrentIndex(index); } return true; } } return false; } bool TabBox::handleMouseEvent(xcb_motion_notify_event_t *e) { xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects if (effects && static_cast(effects)->checkInputWindowEvent(e)) return true; } return false; } bool TabBox::handleMouseEvent(QMouseEvent *event) { if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects if (effects && static_cast(effects)->checkInputWindowEvent(event)) { return true; } } switch (event->type()) { case QEvent::MouseMove: if (!m_tabBox->containsPos(event->globalPos())) { // filter out all events which are not on the TabBox window. // We don't want windows to react on the mouse events return true; } return false; case QEvent::MouseButtonPress: if ((!m_isShown && isDisplayed()) || !m_tabBox->containsPos(event->globalPos())) { close(); // click outside closes tab return true; } // fall through case QEvent::MouseButtonRelease: default: // we do not filter it out, the intenal filter takes care return false; } return false; } bool TabBox::handleWheelEvent(QWheelEvent *event) { if (!m_isShown && isDisplayed()) { // tabbox has been replaced, check effects if (effects && static_cast(effects)->checkInputWindowEvent(event)) { return true; } } if (event->angleDelta().y() == 0) { return false; } const QModelIndex index = m_tabBox->nextPrev(event->angleDelta().y() > 0); if (index.isValid()) { setCurrentIndex(index); } return true; } void TabBox::grabbedKeyEvent(QKeyEvent* event) { emit tabBoxKeyEvent(event); if (!m_isShown && isDisplayed()) { // 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) { accept(); return; } } m_tabBox->grabbedKeyEvent(event); } struct KeySymbolsDeleter { static inline void cleanup(xcb_key_symbols_t *symbols) { xcb_key_symbols_free(symbols); } }; /*! Handles alt-tab / control-tab */ static bool areKeySymXsDepressed(bool bAll, const uint keySyms[], int nKeySyms) { qCDebug(KWIN_TABBOX) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms; Xcb::QueryKeymap keys; QScopedPointer symbols(xcb_key_symbols_alloc(connection())); if (symbols.isNull() || !keys) { return false; } const auto keymap = keys->keys; for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { uint keySymX = keySyms[ iKeySym ]; xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(symbols.data(), keySymX); if (!keyCodes) { continue; } xcb_keycode_t keyCodeX = keyCodes[0]; free(keyCodes); if (keyCodeX == XCB_NO_SYMBOL) { continue; } int i = keyCodeX / 8; char mask = 1 << (keyCodeX - (i * 8)); // Abort if bad index value, if (i < 0 || i >= 32) return false; qCDebug(KWIN_TABBOX) << iKeySym << ": keySymX=0x" << QString::number(keySymX, 16) << " i=" << i << " mask=0x" << QString::number(mask, 16) << " keymap[i]=0x" << QString::number(keymap[i], 16); // If ALL keys passed need to be depressed, if (bAll) { if ((keymap[i] & mask) == 0) return false; } else { // If we are looking for ANY key press, and this key is depressed, if (keymap[i] & mask) return true; } } // If we were looking for ANY key press, then none was found, return false, // If we were looking for ALL key presses, then all were found, return true. return bAll; } static bool areModKeysDepressedX11(const QKeySequence &seq) { uint rgKeySyms[10]; int nKeySyms = 0; int mod = seq[seq.count()-1] & Qt::KeyboardModifierMask; if (mod & Qt::SHIFT) { rgKeySyms[nKeySyms++] = XK_Shift_L; rgKeySyms[nKeySyms++] = XK_Shift_R; } if (mod & Qt::CTRL) { rgKeySyms[nKeySyms++] = XK_Control_L; rgKeySyms[nKeySyms++] = XK_Control_R; } if (mod & Qt::ALT) { rgKeySyms[nKeySyms++] = XK_Alt_L; rgKeySyms[nKeySyms++] = XK_Alt_R; } if (mod & Qt::META) { // It would take some code to determine whether the Win key // is associated with Super or Meta, so check for both. // See bug #140023 for details. rgKeySyms[nKeySyms++] = XK_Super_L; rgKeySyms[nKeySyms++] = XK_Super_R; rgKeySyms[nKeySyms++] = XK_Meta_L; rgKeySyms[nKeySyms++] = XK_Meta_R; } return areKeySymXsDepressed(false, rgKeySyms, nKeySyms); } static bool areModKeysDepressedWayland(const QKeySequence &seq) { const int mod = seq[seq.count()-1] & Qt::KeyboardModifierMask; const Qt::KeyboardModifiers mods = input()->modifiersRelevantForGlobalShortcuts(); if ((mod & Qt::SHIFT) && mods.testFlag(Qt::ShiftModifier)) { return true; } if ((mod & Qt::CTRL) && mods.testFlag(Qt::ControlModifier)) { return true; } if ((mod & Qt::ALT) && mods.testFlag(Qt::AltModifier)) { return true; } if ((mod & Qt::META) && mods.testFlag(Qt::MetaModifier)) { return true; } return false; } static bool areModKeysDepressed(const QKeySequence& seq) { if (seq.isEmpty()) return false; if (kwinApp()->shouldUseWaylandForCompositing()) { return areModKeysDepressedWayland(seq); } else { return areModKeysDepressedX11(seq); } } void TabBox::navigatingThroughWindows(bool forward, const QKeySequence &shortcut, TabBoxMode mode) { if (!m_ready || isGrabbed() || !Workspace::self()->isOnCurrentHead()) { return; } if (!options->focusPolicyIsReasonable()) { //ungrabXKeyboard(); // need that because of accelerator raw mode // CDE style raise / lower CDEWalkThroughWindows(forward); } else { if (areModKeysDepressed(shortcut)) { if (startKDEWalkThroughWindows(mode)) KDEWalkThroughWindows(forward); } else // if the shortcut has no modifiers, don't show the tabbox, // don't grab, but simply go to the next window KDEOneStepThroughWindows(forward, mode); } } void TabBox::slotWalkThroughWindows() { navigatingThroughWindows(true, m_cutWalkThroughWindows, TabBoxWindowsMode); } void TabBox::slotWalkBackThroughWindows() { navigatingThroughWindows(false, m_cutWalkThroughWindowsReverse, TabBoxWindowsMode); } void TabBox::slotWalkThroughWindowsAlternative() { navigatingThroughWindows(true, m_cutWalkThroughWindowsAlternative, TabBoxWindowsAlternativeMode); } void TabBox::slotWalkBackThroughWindowsAlternative() { navigatingThroughWindows(false, m_cutWalkThroughWindowsAlternativeReverse, TabBoxWindowsAlternativeMode); } void TabBox::slotWalkThroughCurrentAppWindows() { navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindows, TabBoxCurrentAppWindowsMode); } void TabBox::slotWalkBackThroughCurrentAppWindows() { navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsReverse, TabBoxCurrentAppWindowsMode); } void TabBox::slotWalkThroughCurrentAppWindowsAlternative() { navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindowsAlternative, TabBoxCurrentAppWindowsAlternativeMode); } void TabBox::slotWalkBackThroughCurrentAppWindowsAlternative() { navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsAlternativeReverse, TabBoxCurrentAppWindowsAlternativeMode); } void TabBox::slotWalkThroughDesktops() { if (!m_ready || isGrabbed() || !Workspace::self()->isOnCurrentHead()) { return; } if (areModKeysDepressed(m_cutWalkThroughDesktops)) { if (startWalkThroughDesktops()) walkThroughDesktops(true); } else { oneStepThroughDesktops(true); } } void TabBox::slotWalkBackThroughDesktops() { if (!m_ready || isGrabbed() || !Workspace::self()->isOnCurrentHead()) { return; } if (areModKeysDepressed(m_cutWalkThroughDesktopsReverse)) { if (startWalkThroughDesktops()) walkThroughDesktops(false); } else { oneStepThroughDesktops(false); } } void TabBox::slotWalkThroughDesktopList() { if (!m_ready || isGrabbed() || !Workspace::self()->isOnCurrentHead()) { return; } if (areModKeysDepressed(m_cutWalkThroughDesktopList)) { if (startWalkThroughDesktopList()) walkThroughDesktops(true); } else { oneStepThroughDesktopList(true); } } void TabBox::slotWalkBackThroughDesktopList() { if (!m_ready || isGrabbed() || !Workspace::self()->isOnCurrentHead()) { return; } if (areModKeysDepressed(m_cutWalkThroughDesktopListReverse)) { if (startWalkThroughDesktopList()) walkThroughDesktops(false); } else { oneStepThroughDesktopList(false); } } void TabBox::shadeActivate(AbstractClient *c) { if ((c->shadeMode() == ShadeNormal || c->shadeMode() == ShadeHover) && options->isShadeHover()) c->setShade(ShadeActivated); } bool TabBox::toggle(ElectricBorder eb) { if (m_borderAlternativeActivate.contains(eb)) { return toggleMode(TabBoxWindowsAlternativeMode); } else { return toggleMode(TabBoxWindowsMode); } } bool TabBox::toggleMode(TabBoxMode mode) { if (!options->focusPolicyIsReasonable()) return false; // not supported. if (isDisplayed()) { accept(); return true; } if (!establishTabBoxGrab()) return false; m_noModifierGrab = m_tabGrab = true; setMode(mode); reset(); show(); return true; } bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_tabGrab = true; m_noModifierGrab = false; setMode(mode); reset(); return true; } bool TabBox::startWalkThroughDesktops(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_desktopGrab = true; m_noModifierGrab = false; setMode(mode); reset(); return true; } bool TabBox::startWalkThroughDesktops() { return startWalkThroughDesktops(TabBoxDesktopMode); } bool TabBox::startWalkThroughDesktopList() { return startWalkThroughDesktops(TabBoxDesktopListMode); } void TabBox::KDEWalkThroughWindows(bool forward) { nextPrev(forward); delayedShow(); } void TabBox::walkThroughDesktops(bool forward) { nextPrev(forward); delayedShow(); } void TabBox::CDEWalkThroughWindows(bool forward) { Client* c = nullptr; // this function find the first suitable client for unreasonable focus // policies - the topmost one, with some exceptions (can't be keepabove/below, // otherwise it gets stuck on them) // Q_ASSERT(Workspace::self()->block_stacking_updates == 0); for (int i = Workspace::self()->stackingOrder().size() - 1; i >= 0 ; --i) { Client* it = qobject_cast(Workspace::self()->stackingOrder().at(i)); if (it && it->isOnCurrentActivity() && it->isOnCurrentDesktop() && !it->isSpecialWindow() && it->isShown(false) && it->wantsTabFocus() && !it->keepAbove() && !it->keepBelow()) { c = it; break; } } Client* nc = c; bool options_traverse_all; { KConfigGroup group(kwinApp()->config(), "TabBox"); options_traverse_all = group.readEntry("TraverseAll", false); } Client* firstClient = nullptr; do { nc = forward ? nextClientStatic(nc) : previousClientStatic(nc); if (!firstClient) { // When we see our first client for the second time, // it's time to stop. firstClient = nc; } else if (nc == firstClient) { // No candidates found. nc = nullptr; break; } } while (nc && nc != c && ((!options_traverse_all && !nc->isOnDesktop(currentDesktop())) || nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() || !nc->isOnCurrentActivity())); if (nc) { if (c && c != nc) Workspace::self()->lowerClient(c); if (options->focusPolicyIsReasonable()) { Workspace::self()->activateClient(nc); shadeActivate(nc); } else { if (!nc->isOnDesktop(currentDesktop())) setCurrentDesktop(nc->desktop()); Workspace::self()->raiseClient(nc); } } } void TabBox::KDEOneStepThroughWindows(bool forward, TabBoxMode mode) { setMode(mode); reset(); nextPrev(forward); if (AbstractClient* c = currentClient()) { Workspace::self()->activateClient(c); shadeActivate(c); } } void TabBox::oneStepThroughDesktops(bool forward, TabBoxMode mode) { setMode(mode); reset(); nextPrev(forward); if (currentDesktop() != -1) setCurrentDesktop(currentDesktop()); } void TabBox::oneStepThroughDesktops(bool forward) { oneStepThroughDesktops(forward, TabBoxDesktopMode); } void TabBox::oneStepThroughDesktopList(bool forward) { oneStepThroughDesktops(forward, TabBoxDesktopListMode); } /*! Handles holding alt-tab / control-tab */ void TabBox::keyPress(int keyQt) { enum Direction { Backward = -1, Steady = 0, Forward = 1 }; Direction direction(Steady); auto contains = [](const QKeySequence &shortcut, int key) -> bool { for (int i = 0; i < shortcut.count(); ++i) { if (shortcut[i] == key) { return true; } } return false; }; // tests whether a shortcut matches and handles pitfalls on ShiftKey invocation auto directionFor = [keyQt, contains](const QKeySequence &forward, const QKeySequence &backward) -> Direction { if (contains(forward, keyQt)) return Forward; if (contains(backward, keyQt)) return Backward; if (!(keyQt & Qt::ShiftModifier)) return Steady; // Before testing the unshifted key (Ctrl+A vs. Ctrl+Shift+a etc.), see whether this is +Shift+Tab // and check that against +Shift+Backtab (as well) Qt::KeyboardModifiers mods = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier|Qt::MetaModifier|Qt::KeypadModifier|Qt::GroupSwitchModifier; mods &= keyQt; if ((keyQt & ~mods) == Qt::Key_Tab) { if (contains(forward, mods | Qt::Key_Backtab)) return Forward; if (contains(backward, mods | Qt::Key_Backtab)) return Backward; } // if the shortcuts do not match, try matching again after filtering the shift key from keyQt // it is needed to handle correctly the ALT+~ shorcut for example as it is coded as ALT+SHIFT+~ in keyQt if (contains(forward, keyQt & ~Qt::ShiftModifier)) return Forward; if (contains(backward, keyQt & ~Qt::ShiftModifier)) return Backward; return Steady; }; if (m_tabGrab) { static const int ModeCount = 4; static const TabBoxMode modes[ModeCount] = { TabBoxWindowsMode, TabBoxWindowsAlternativeMode, TabBoxCurrentAppWindowsMode, TabBoxCurrentAppWindowsAlternativeMode }; static const QKeySequence cuts[2*ModeCount] = { // forward m_cutWalkThroughWindows, m_cutWalkThroughWindowsAlternative, m_cutWalkThroughCurrentAppWindows, m_cutWalkThroughCurrentAppWindowsAlternative, // backward m_cutWalkThroughWindowsReverse, m_cutWalkThroughWindowsAlternativeReverse, m_cutWalkThroughCurrentAppWindowsReverse, m_cutWalkThroughCurrentAppWindowsAlternativeReverse }; bool testedCurrent = false; // in case of collision, prefer to stay in the current mode int i = 0, j = 0; while (true) { if (!testedCurrent && modes[i] != mode()) { ++j; i = (i+1) % ModeCount; continue; } if (testedCurrent && modes[i] == mode()) { break; } testedCurrent = true; direction = directionFor(cuts[i], cuts[i+ModeCount]); if (direction != Steady) { if (modes[i] != mode()) { accept(false); setMode(modes[i]); auto replayWithChangedTabboxMode = [this, direction]() { reset(); nextPrev(direction == Forward); }; QTimer::singleShot(50, this, replayWithChangedTabboxMode); } break; } else if (++j > ModeCount) { // guarding counter for invalid modes qCDebug(KWIN_TABBOX) << "Invalid TabBoxMode"; return; } i = (i+1) % ModeCount; } if (direction != Steady) { qCDebug(KWIN_TABBOX) << "== " << cuts[i].toString() << " or " << cuts[i+ModeCount].toString(); KDEWalkThroughWindows(direction == Forward); } } else if (m_desktopGrab) { direction = directionFor(m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse); if (direction == Steady) direction = directionFor(m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse); if (direction != Steady) walkThroughDesktops(direction == Forward); } if (m_desktopGrab || m_tabGrab) { if (((keyQt & ~Qt::KeyboardModifierMask) == Qt::Key_Escape) && direction == Steady) { // if Escape is part of the shortcut, don't cancel close(true); } else if (direction == Steady) { QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, keyQt & ~Qt::KeyboardModifierMask, Qt::NoModifier); grabbedKeyEvent(event); } } } void TabBox::close(bool abort) { if (isGrabbed()) { removeTabBoxGrab(); } hide(abort); m_tabGrab = false; m_desktopGrab = false; m_noModifierGrab = false; } void TabBox::accept(bool closeTabBox) { AbstractClient *c = currentClient(); if (closeTabBox) close(); if (c) { Workspace::self()->activateClient(c); shadeActivate(c); if (c->isDesktop()) Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); } } /*! Handles alt-tab / control-tab releasing */ void TabBox::keyRelease(const xcb_key_release_event_t *ev) { if (m_noModifierGrab) { return; } unsigned int mk = ev->state & (KKeyServer::modXShift() | KKeyServer::modXCtrl() | KKeyServer::modXAlt() | KKeyServer::modXMeta()); // ev.state is state before the key release, so just checking mk being 0 isn't enough // using XQueryPointer() also doesn't seem to work well, so the check that all // modifiers are released: only one modifier is active and the currently released // key is this modifier - if yes, release the grab int mod_index = -1; for (int i = XCB_MAP_INDEX_SHIFT; i <= XCB_MAP_INDEX_5; ++i) if ((mk & (1 << i)) != 0) { if (mod_index >= 0) return; mod_index = i; } bool release = false; if (mod_index == -1) release = true; else { Xcb::ModifierMapping xmk; if (xmk) { xcb_keycode_t *keycodes = xmk.keycodes(); const int maxIndex = xmk.size(); for (int i = 0; i < xmk->keycodes_per_modifier; ++i) { const int index = xmk->keycodes_per_modifier * mod_index + i; if (index >= maxIndex) { continue; } if (keycodes[index] == ev->detail) { release = true; } } } } if (!release) return; if (m_tabGrab) { bool old_control_grab = m_desktopGrab; accept(); m_desktopGrab = old_control_grab; } if (m_desktopGrab) { bool old_tab_grab = m_tabGrab; int desktop = currentDesktop(); close(); m_tabGrab = old_tab_grab; if (desktop != -1) { setCurrentDesktop(desktop); VirtualDesktopManager::self()->setCurrent(desktop); } } } void TabBox::modifiersReleased() { if (m_noModifierGrab) { return; } if (m_tabGrab) { bool old_control_grab = m_desktopGrab; accept(); m_desktopGrab = old_control_grab; } if (m_desktopGrab) { bool old_tab_grab = m_tabGrab; int desktop = currentDesktop(); close(); m_tabGrab = old_tab_grab; if (desktop != -1) { setCurrentDesktop(desktop); VirtualDesktopManager::self()->setCurrent(desktop); } } } int TabBox::nextDesktopStatic(int iDesktop) const { DesktopNext functor; return functor(iDesktop, true); } int TabBox::previousDesktopStatic(int iDesktop) const { DesktopPrevious functor; return functor(iDesktop, true); } /*! auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ Client* TabBox::nextClientStatic(Client* c) const { if (!c || Workspace::self()->clientList().isEmpty()) return 0; int pos = Workspace::self()->clientList().indexOf(c); if (pos == -1) return Workspace::self()->clientList().first(); ++pos; if (pos == Workspace::self()->clientList().count()) return Workspace::self()->clientList().first(); return Workspace::self()->clientList()[ pos ]; } /*! auxiliary functions to travers all clients according to the static order. Useful for the CDE-style Alt-tab feature. */ Client* TabBox::previousClientStatic(Client* c) const { if (!c || Workspace::self()->clientList().isEmpty()) return 0; int pos = Workspace::self()->clientList().indexOf(c); if (pos == -1) return Workspace::self()->clientList().last(); if (pos == 0) return Workspace::self()->clientList().last(); --pos; return Workspace::self()->clientList()[ pos ]; } bool TabBox::establishTabBoxGrab() { if (kwinApp()->shouldUseWaylandForCompositing()) { m_forcedGlobalMouseGrab = true; return true; } updateXTime(); if (!grabXKeyboard()) return false; // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent // using Alt+Tab while DND (#44972). However force passive grabs on all windows // in order to catch MouseRelease events and close the tabbox (#67416). // All clients already have passive grabs in their wrapper windows, so check only // the active client, which may not have it. assert(!m_forcedGlobalMouseGrab); m_forcedGlobalMouseGrab = true; if (Workspace::self()->activeClient() != nullptr) Workspace::self()->activeClient()->updateMouseGrab(); return true; } void TabBox::removeTabBoxGrab() { if (kwinApp()->shouldUseWaylandForCompositing()) { m_forcedGlobalMouseGrab = false; return; } updateXTime(); ungrabXKeyboard(); assert(m_forcedGlobalMouseGrab); m_forcedGlobalMouseGrab = false; if (Workspace::self()->activeClient() != nullptr) Workspace::self()->activeClient()->updateMouseGrab(); } } // namespace TabBox } // namespace diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index bdc02a6fc..012cd815d 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -1,295 +1,300 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 Martin Gräßlin 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 . *********************************************************************/ #ifndef KWIN_TABBOX_H #define KWIN_TABBOX_H #include #include #include #include "utils.h" #include "tabbox/tabboxhandler.h" class KConfigGroup; class QAction; class QMouseEvent; class QKeyEvent; class QWheelEvent; struct xcb_button_press_event_t; struct xcb_motion_notify_event_t; namespace KWin { class Workspace; class AbstractClient; class Client; namespace TabBox { class DesktopChainManager; class TabBoxConfig; class TabBox; class TabBoxHandlerImpl : public TabBoxHandler { public: explicit TabBoxHandlerImpl(TabBox* tabBox); virtual ~TabBoxHandlerImpl(); virtual int activeScreen() const; virtual QWeakPointer< TabBoxClient > activeClient() const; virtual int currentDesktop() const; virtual QString desktopName(TabBoxClient* client) const; virtual QString desktopName(int desktop) const; virtual bool isKWinCompositing() const; virtual QWeakPointer< TabBoxClient > nextClientFocusChain(TabBoxClient* client) const; virtual QWeakPointer< TabBoxClient > firstClientFocusChain() const; virtual bool isInFocusChain (TabBoxClient* client) const; virtual int nextDesktopFocusChain(int desktop) const; virtual int numberOfDesktops() const; virtual TabBoxClientList stackingOrder() const; virtual void elevateClient(TabBoxClient* c, QWindow *tabbox, bool elevate) const; virtual void raiseClient(TabBoxClient *client) const; virtual void restack(TabBoxClient *c, TabBoxClient *under); virtual void shadeClient(TabBoxClient *c, bool b) const; virtual QWeakPointer< TabBoxClient > clientToAddToList(KWin::TabBox::TabBoxClient* client, int desktop) const; virtual QWeakPointer< TabBoxClient > desktopClient() const; virtual void activateAndClose(); void highlightWindows(TabBoxClient *window = nullptr, QWindow *controller = nullptr) override; + bool noModifierGrab() const override; private: bool checkDesktop(TabBoxClient* client, int desktop) const; bool checkActivity(TabBoxClient* client) const; bool checkApplications(TabBoxClient* client) const; bool checkMinimized(TabBoxClient* client) const; bool checkMultiScreen(TabBoxClient* client) const; TabBox* m_tabBox; DesktopChainManager* m_desktopFocusChain; }; class TabBoxClientImpl : public TabBoxClient { public: explicit TabBoxClientImpl(AbstractClient *client); virtual ~TabBoxClientImpl(); virtual QString caption() const; virtual QIcon icon() const override; virtual WId window() const; virtual bool isMinimized() const; virtual int x() const; virtual int y() const; virtual int width() const; virtual int height() const; virtual bool isCloseable() const; virtual void close(); virtual bool isFirstInTabBox() const; AbstractClient* client() const { return m_client; } private: AbstractClient* m_client; }; class KWIN_EXPORT TabBox : public QObject { Q_OBJECT public: ~TabBox(); AbstractClient *currentClient(); QList currentClientList(); int currentDesktop(); QList< int > currentDesktopList(); void setCurrentClient(AbstractClient *newClient); void setCurrentDesktop(int newDesktop); void setMode(TabBoxMode mode); TabBoxMode mode() const { return m_tabBoxMode; } void reset(bool partial_reset = false); void nextPrev(bool next = true); void delayedShow(); void hide(bool abort = false); /*! Increase the reference count, preventing the default tabbox from showing. \sa unreference(), isDisplayed() */ void reference() { ++m_displayRefcount; } /*! Decrease the reference count. Only when the reference count is 0 will the default tab box be shown. */ void unreference() { --m_displayRefcount; } /*! Returns whether the tab box is being displayed, either natively or by an effect. \sa reference(), unreference() */ bool isDisplayed() const { return m_displayRefcount > 0; }; bool handleMouseEvent(xcb_button_press_event_t *e); bool handleMouseEvent(xcb_motion_notify_event_t *e); bool handleMouseEvent(QMouseEvent *event); bool handleWheelEvent(QWheelEvent *event); void grabbedKeyEvent(QKeyEvent* event); bool isGrabbed() const { return m_tabGrab || m_desktopGrab; }; void initShortcuts(); Client* nextClientStatic(Client*) const; Client* previousClientStatic(Client*) const; int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; void keyPress(int key); void keyRelease(const xcb_key_release_event_t *ev); void modifiersReleased(); bool forcedGlobalMouseGrab() const { return m_forcedGlobalMouseGrab; } + bool noModifierGrab() const { + return m_noModifierGrab; + } + static TabBox *self(); static TabBox *create(QObject *parent); public Q_SLOTS: void show(); void close(bool abort = false); void accept(bool closeTabBox = true); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); void slotWalkBackThroughDesktopList(); void slotWalkThroughWindows(); void slotWalkBackThroughWindows(); void slotWalkThroughWindowsAlternative(); void slotWalkBackThroughWindowsAlternative(); void slotWalkThroughCurrentAppWindows(); void slotWalkBackThroughCurrentAppWindows(); void slotWalkThroughCurrentAppWindowsAlternative(); void slotWalkBackThroughCurrentAppWindowsAlternative(); void handlerReady(); bool toggle(ElectricBorder eb); Q_SIGNALS: void tabBoxAdded(int); void tabBoxClosed(); void tabBoxUpdated(); void tabBoxKeyEvent(QKeyEvent*); private: explicit TabBox(QObject *parent); void setCurrentIndex(QModelIndex index, bool notifyEffects = true); void loadConfig(const KConfigGroup& config, TabBoxConfig& tabBoxConfig); bool startKDEWalkThroughWindows(TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode bool startWalkThroughDesktops(TabBoxMode mode); // TabBoxDesktopMode | TabBoxDesktopListMode bool startWalkThroughDesktops(); bool startWalkThroughDesktopList(); void navigatingThroughWindows(bool forward, const QKeySequence &shortcut, TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void KDEWalkThroughWindows(bool forward); void CDEWalkThroughWindows(bool forward); void walkThroughDesktops(bool forward); void KDEOneStepThroughWindows(bool forward, TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void oneStepThroughDesktops(bool forward, TabBoxMode mode); // TabBoxDesktopMode | TabBoxDesktopListMode void oneStepThroughDesktops(bool forward); void oneStepThroughDesktopList(bool forward); bool establishTabBoxGrab(); void removeTabBoxGrab(); template void key(const char *actionName, Slot slot, const QKeySequence &shortcut = QKeySequence()); void shadeActivate(AbstractClient *c); bool toggleMode(TabBoxMode mode); private Q_SLOTS: void reconfigure(); void globalShortcutChanged(QAction *action, const QKeySequence &seq); private: TabBoxMode m_tabBoxMode; TabBoxHandlerImpl* m_tabBox; bool m_delayShow; int m_delayShowTime; QTimer m_delayedShowTimer; int m_displayRefcount; TabBoxConfig m_defaultConfig; TabBoxConfig m_alternativeConfig; TabBoxConfig m_defaultCurrentApplicationConfig; TabBoxConfig m_alternativeCurrentApplicationConfig; TabBoxConfig m_desktopConfig; TabBoxConfig m_desktopListConfig; // false if an effect has referenced the tabbox // true if tabbox is active (independent on showTabbox setting) 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; QKeySequence m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse; QKeySequence m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse; QKeySequence m_cutWalkThroughWindows, m_cutWalkThroughWindowsReverse; QKeySequence m_cutWalkThroughWindowsAlternative, m_cutWalkThroughWindowsAlternativeReverse; QKeySequence m_cutWalkThroughCurrentAppWindows, m_cutWalkThroughCurrentAppWindowsReverse; QKeySequence m_cutWalkThroughCurrentAppWindowsAlternative, m_cutWalkThroughCurrentAppWindowsAlternativeReverse; bool m_forcedGlobalMouseGrab; bool m_ready; // indicates whether the config is completely loaded QList m_borderActivate, m_borderAlternativeActivate; QHash m_touchActivate; QHash m_touchAlternativeActivate; static TabBox *s_self; }; inline TabBox *TabBox::self() { return s_self; } } // namespace TabBox } // namespace #endif diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index afaf5a12f..9edfca2e5 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -1,656 +1,657 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Martin Gräßlin 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 . *********************************************************************/ // own #include "tabboxhandler.h" #include #include #include "xcbutils.h" // tabbox #include "clientmodel.h" #include "desktopmodel.h" #include "tabboxconfig.h" #include "thumbnailitem.h" #include "scripting/scripting.h" #include "switcheritem.h" #include "tabbox_logging.h" // Qt #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include namespace KWin { namespace TabBox { class TabBoxHandlerPrivate { public: TabBoxHandlerPrivate(TabBoxHandler *q); ~TabBoxHandlerPrivate(); /** * Updates the current highlight window state */ void updateHighlightWindows(); /** * Ends window highlighting */ void endHighlightWindows(bool abort = false); void show(); QQuickWindow *window() const; SwitcherItem *switcherItem() const; ClientModel* clientModel() const; DesktopModel* desktopModel() const; TabBoxHandler *q; // public pointer // members TabBoxConfig config; QScopedPointer m_qmlContext; QScopedPointer m_qmlComponent; QObject *m_mainItem; QMap m_clientTabBoxes; QMap m_desktopTabBoxes; ClientModel* m_clientModel; DesktopModel* m_desktopModel; QModelIndex index; /** * Indicates if the tabbox is shown. */ bool isShown; TabBoxClient *lastRaisedClient, *lastRaisedClientSucc; int wheelAngleDelta = 0; private: QObject *createSwitcherItem(bool desktopMode); }; TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : m_qmlContext() , m_qmlComponent() , m_mainItem(nullptr) { this->q = q; isShown = false; lastRaisedClient = nullptr; lastRaisedClientSucc = nullptr; config = TabBoxConfig(); m_clientModel = new ClientModel(q); m_desktopModel = new DesktopModel(q); } TabBoxHandlerPrivate::~TabBoxHandlerPrivate() { for (auto it = m_clientTabBoxes.constBegin(); it != m_clientTabBoxes.constEnd(); ++it) { delete it.value(); } for (auto it = m_desktopTabBoxes.constBegin(); it != m_desktopTabBoxes.constEnd(); ++it) { delete it.value(); } } QQuickWindow *TabBoxHandlerPrivate::window() const { if (!m_mainItem) { return nullptr; } if (QQuickWindow *w = qobject_cast(m_mainItem)) { return w; } return m_mainItem->findChild(); } #ifndef KWIN_UNIT_TEST SwitcherItem *TabBoxHandlerPrivate::switcherItem() const { if (!m_mainItem) { return nullptr; } if (SwitcherItem *i = qobject_cast(m_mainItem)) { return i; } else if (QQuickWindow *w = qobject_cast(m_mainItem)) { return w->contentItem()->findChild(); } return m_mainItem->findChild(); } #endif ClientModel* TabBoxHandlerPrivate::clientModel() const { return m_clientModel; } DesktopModel* TabBoxHandlerPrivate::desktopModel() const { return m_desktopModel; } void TabBoxHandlerPrivate::updateHighlightWindows() { if (!isShown || config.tabBoxMode() != TabBoxConfig::ClientTabBox) return; TabBoxClient *currentClient = q->client(index); QWindow *w = window(); if (q->isKWinCompositing()) { if (lastRaisedClient) q->elevateClient(lastRaisedClient, w, false); lastRaisedClient = currentClient; if (currentClient) q->elevateClient(currentClient, w, true); } else { if (lastRaisedClient) { q->shadeClient(lastRaisedClient, true); if (lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); // TODO lastRaisedClient->setMinimized( lastRaisedClientWasMinimized ); } lastRaisedClient = currentClient; if (lastRaisedClient) { q->shadeClient(lastRaisedClient, false); // TODO if ( (lastRaisedClientWasMinimized = lastRaisedClient->isMinimized()) ) // lastRaisedClient->setMinimized( false ); TabBoxClientList order = q->stackingOrder(); int succIdx = order.count() + 1; for (int i=0; iraiseClient(lastRaisedClient); } } if (config.isShowTabBox() && w) { q->highlightWindows(currentClient, w); } else { q->highlightWindows(currentClient); } } void TabBoxHandlerPrivate::endHighlightWindows(bool abort) { TabBoxClient *currentClient = q->client(index); if (config.isHighlightWindows() && q->isKWinCompositing()) { foreach (const QWeakPointer &clientPointer, q->stackingOrder()) { if (QSharedPointer client = clientPointer.toStrongRef()) if (client != currentClient) // to not mess up with wanted ShadeActive/ShadeHover state q->shadeClient(client.data(), true); } } QWindow *w = window(); if (currentClient) q->elevateClient(currentClient, w, false); if (abort && lastRaisedClient && lastRaisedClientSucc) q->restack(lastRaisedClient, lastRaisedClientSucc); lastRaisedClient = nullptr; lastRaisedClientSucc = nullptr; // highlight windows q->highlightWindows(); } #ifndef KWIN_UNIT_TEST QObject *TabBoxHandlerPrivate::createSwitcherItem(bool desktopMode) { // first try look'n'feel package QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/%1/contents/%2") .arg(config.layoutName()) .arg(desktopMode ? QStringLiteral("desktopswitcher/DesktopSwitcher.qml") : QStringLiteral("windowswitcher/WindowSwitcher.qml"))); if (file.isNull()) { const QString folderName = QLatin1String(KWIN_NAME) + (desktopMode ? QLatin1String("/desktoptabbox/") : QLatin1String("/tabbox/")); auto findSwitcher = [this, desktopMode, folderName] { const QString type = desktopMode ? QStringLiteral("KWin/DesktopSwitcher") : QStringLiteral("KWin/WindowSwitcher"); auto offers = KPackage::PackageLoader::self()->findPackages(type, folderName, [this] (const KPluginMetaData &data) { return data.pluginId().compare(config.layoutName(), Qt::CaseInsensitive) == 0; } ); if (offers.isEmpty()) { // load default offers = KPackage::PackageLoader::self()->findPackages(type, folderName, [this] (const KPluginMetaData &data) { return data.pluginId().compare(QStringLiteral("informative"), Qt::CaseInsensitive) == 0; } ); if (offers.isEmpty()) { qCDebug(KWIN_TABBOX) << "could not find default window switcher layout"; return KPluginMetaData(); } } return offers.first(); }; auto service = findSwitcher(); if (!service.isValid()) { return nullptr; } if (service.value(QStringLiteral("X-Plasma-API")) != QLatin1String("declarativeappletscript")) { qCDebug(KWIN_TABBOX) << "Window Switcher Layout is no declarativeappletscript"; return nullptr; } auto findScriptFile = [desktopMode, service, folderName] { const QString pluginName = service.pluginId(); const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript")); return QStandardPaths::locate(QStandardPaths::GenericDataLocation, folderName + pluginName + QLatin1String("/contents/") + scriptName); }; file = findScriptFile(); } if (file.isNull()) { qCDebug(KWIN_TABBOX) << "Could not find QML file for window switcher"; return nullptr; } m_qmlComponent->loadUrl(QUrl::fromLocalFile(file)); if (m_qmlComponent->isError()) { qCDebug(KWIN_TABBOX) << "Component failed to load: " << m_qmlComponent->errors(); QStringList args; args << QStringLiteral("--passivepopup") << i18n("The Window Switcher installation is broken, resources are missing.\n" "Contact your distribution about this.") << QStringLiteral("20"); KProcess::startDetached(QStringLiteral("kdialog"), args); } else { QObject *object = m_qmlComponent->create(m_qmlContext.data()); if (desktopMode) { m_desktopTabBoxes.insert(config.layoutName(), object); } else { m_clientTabBoxes.insert(config.layoutName(), object); } return object; } return nullptr; } #endif void TabBoxHandlerPrivate::show() { #ifndef KWIN_UNIT_TEST if (m_qmlContext.isNull()) { qmlRegisterType("org.kde.kwin", 2, 0, "Switcher"); m_qmlContext.reset(new QQmlContext(Scripting::self()->qmlEngine())); } if (m_qmlComponent.isNull()) { m_qmlComponent.reset(new QQmlComponent(Scripting::self()->qmlEngine())); } const bool desktopMode = (config.tabBoxMode() == TabBoxConfig::DesktopTabBox); auto findMainItem = [this](const QMap &tabBoxes) -> QObject* { auto it = tabBoxes.constFind(config.layoutName()); if (it != tabBoxes.constEnd()) { return it.value(); } return nullptr; }; m_mainItem = nullptr; m_mainItem = desktopMode ? findMainItem(m_desktopTabBoxes) : findMainItem(m_clientTabBoxes); if (!m_mainItem) { m_mainItem = createSwitcherItem(desktopMode); if (!m_mainItem) { return; } } if (SwitcherItem *item = switcherItem()) { // In case the model isn't yet set (see below), index will be reset and therefore we // need to save the current index row (https://bugs.kde.org/show_bug.cgi?id=333511). int indexRow = index.row(); if (!item->model()) { QAbstractItemModel *model = nullptr; if (desktopMode) { model = desktopModel(); } else { model = clientModel(); } item->setModel(model); } item->setAllDesktops(config.clientDesktopMode() == TabBoxConfig::AllDesktopsClients); item->setCurrentIndex(indexRow); + item->setNoModifierGrab(q->noModifierGrab()); // everything is prepared, so let's make the whole thing visible item->setVisible(true); } if (QWindow *w = window()) { wheelAngleDelta = 0; w->installEventFilter(q); } #endif } /*********************************************** * TabBoxHandler ***********************************************/ TabBoxHandler::TabBoxHandler(QObject *parent) : QObject(parent) { KWin::TabBox::tabBox = this; d = new TabBoxHandlerPrivate(this); } TabBoxHandler::~TabBoxHandler() { delete d; } const KWin::TabBox::TabBoxConfig& TabBoxHandler::config() const { return d->config; } void TabBoxHandler::setConfig(const TabBoxConfig& config) { d->config = config; emit configChanged(); } void TabBoxHandler::show() { d->isShown = true; d->lastRaisedClient = nullptr; d->lastRaisedClientSucc = nullptr; if (d->config.isShowTabBox()) { d->show(); } if (d->config.isHighlightWindows()) { Xcb::sync(); // TODO this should be // QMetaObject::invokeMethod(this, "initHighlightWindows", Qt::QueuedConnection); // but we somehow need to cross > 1 event cycle (likely because of queued invocation in the effects) // to ensure the EffectWindow is present when updateHighlightWindows, thus elevating the window/tabbox QTimer::singleShot(1, this, SLOT(initHighlightWindows())); } } void TabBoxHandler::initHighlightWindows() { if (isKWinCompositing()) { foreach (const QWeakPointer &clientPointer, stackingOrder()) { if (QSharedPointer client = clientPointer.toStrongRef()) shadeClient(client.data(), false); } } d->updateHighlightWindows(); } void TabBoxHandler::hide(bool abort) { d->isShown = false; if (d->config.isHighlightWindows()) { d->endHighlightWindows(abort); } #ifndef KWIN_UNIT_TEST if (SwitcherItem *item = d->switcherItem()) { item->setVisible(false); } #endif if (QQuickWindow *w = d->window()) { w->hide(); w->destroy(); } d->m_mainItem = nullptr; } QModelIndex TabBoxHandler::nextPrev(bool forward) const { QModelIndex ret; QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; default: return d->index; } if (forward) { int column = d->index.column() + 1; int row = d->index.row(); if (column == model->columnCount()) { column = 0; row++; if (row == model->rowCount()) row = 0; } ret = model->index(row, column); if (!ret.isValid()) ret = model->index(0, 0); } else { int column = d->index.column() - 1; int row = d->index.row(); if (column < 0) { column = model->columnCount() - 1; row--; if (row < 0) row = model->rowCount() - 1; } ret = model->index(row, column); if (!ret.isValid()) { row = model->rowCount() - 1; for (int i = model->columnCount() - 1; i >= 0; i--) { ret = model->index(row, i); if (ret.isValid()) break; } } } if (ret.isValid()) return ret; else return d->index; } QModelIndex TabBoxHandler::desktopIndex(int desktop) const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QModelIndex(); return d->desktopModel()->desktopIndex(desktop); } QList< int > TabBoxHandler::desktopList() const { if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) return QList< int >(); return d->desktopModel()->desktopList(); } int TabBoxHandler::desktop(const QModelIndex& index) const { if (!index.isValid() || (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)) return -1; QVariant ret = d->desktopModel()->data(index, DesktopModel::DesktopRole); if (ret.isValid()) return ret.toInt(); else return -1; } void TabBoxHandler::setCurrentIndex(const QModelIndex& index) { if (d->index == index) { return; } if (!index.isValid()) { return; } d->index = index; if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { if (d->config.isHighlightWindows()) { d->updateHighlightWindows(); } } emit selectedIndexChanged(); } const QModelIndex& TabBoxHandler::currentIndex() const { return d->index; } void TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const { if (!d->m_mainItem || !d->window()) { return; } const QList items = d->window()->contentItem()->findChildren(QString(), Qt::FindDirectChildrenOnly); for (QQuickItem *item : items) { d->window()->sendEvent(item, event); if (event->isAccepted()) { break; } } } bool TabBoxHandler::containsPos(const QPoint& pos) const { if (!d->m_mainItem) { return false; } QWindow *w = d->window(); if (w) { return w->geometry().contains(pos); } return false; } QModelIndex TabBoxHandler::index(QWeakPointer client) const { return d->clientModel()->index(client); } TabBoxClientList TabBoxHandler::clientList() const { if (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox) return TabBoxClientList(); return d->clientModel()->clientList(); } TabBoxClient* TabBoxHandler::client(const QModelIndex& index) const { if ((!index.isValid()) || (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox)) return nullptr; TabBoxClient* c = static_cast< TabBoxClient* >( d->clientModel()->data(index, ClientModel::ClientRole).value()); return c; } void TabBoxHandler::createModel(bool partialReset) { switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: { d->clientModel()->createClientList(partialReset); // TODO: C++11 use lambda function bool lastRaised = false; bool lastRaisedSucc = false; foreach (const QWeakPointer &clientPointer, stackingOrder()) { QSharedPointer client = clientPointer.toStrongRef(); if (!client) { continue; } if (client.data() == d->lastRaisedClient) { lastRaised = true; } if (client.data() == d->lastRaisedClientSucc) { lastRaisedSucc = true; } } if (d->lastRaisedClient && !lastRaised) d->lastRaisedClient = nullptr; if (d->lastRaisedClientSucc && !lastRaisedSucc) d->lastRaisedClientSucc = nullptr; break; } case TabBoxConfig::DesktopTabBox: d->desktopModel()->createDesktopList(); break; } } QModelIndex TabBoxHandler::first() const { QAbstractItemModel* model; switch(d->config.tabBoxMode()) { case TabBoxConfig::ClientTabBox: model = d->clientModel(); break; case TabBoxConfig::DesktopTabBox: model = d->desktopModel(); break; default: return QModelIndex(); } return model->index(0, 0); } bool TabBoxHandler::eventFilter(QObject *watched, QEvent *e) { if (e->type() == QEvent::Wheel && watched == d->window()) { QWheelEvent *event = static_cast(e); // On x11 the delta for vertical scrolling might also be on X for whatever reason const int delta = qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y()) ? event->angleDelta().x() : event->angleDelta().y(); d->wheelAngleDelta += delta; while (d->wheelAngleDelta <= -120) { d->wheelAngleDelta += 120; const QModelIndex index = nextPrev(true); if (index.isValid()) { setCurrentIndex(index); } } while (d->wheelAngleDelta >= 120) { d->wheelAngleDelta -= 120; const QModelIndex index = nextPrev(false); if (index.isValid()) { setCurrentIndex(index); } } return true; } // pass on return QObject::eventFilter(watched, e); } TabBoxHandler* tabBox = nullptr; TabBoxClient::TabBoxClient() { } TabBoxClient::~TabBoxClient() { } } // namespace TabBox } // namespace KWin diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index 3400af421..040a2f294 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -1,402 +1,408 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Martin Gräßlin 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 . *********************************************************************/ #ifndef TABBOXHANDLER_H #define TABBOXHANDLER_H #include "tabboxconfig.h" #include #include #include #include #include #include /** * @file * This file contains the classes which hide KWin core from tabbox. * It defines the pure virtual classes TabBoxHandler and TabBoxClient. * The classes have to be implemented in KWin Core. * * @author Martin Gräßlin * @since 4.4 */ class QKeyEvent; namespace KWin { /** * The TabBox is a model based view for displaying a list while switching windows or desktops. * This functionality is mostly referred as Alt+Tab. TabBox itself does not provide support for * switching windows or desktops. This has to be done outside of TabBox inside an independent controller. * * The main entrance point to TabBox is the class TabBoxHandler, which has to be subclassed and implemented. * The class TabBoxClient, which represents a window client inside TabBox, has to be implemented as well. * * The behavior of the TabBox is defined by the TabBoxConfig and has to be set in the TabBoxHandler. * If the TabBox should be used to switch desktops as well as clients it is sufficient to just provide * different TabBoxConfig objects instead of creating an own handler for each mode. * * In order to use the TabBox the TabBoxConfig has to be set. This defines if the model for desktops or for * clients will be used. The model has to be initialized by calling TabBoxHandler::createModel(), as the * model is undefined when the TabBox is not active. The TabBox is activated by TabBoxHandler::show(). * Depending on the current set TabBoxConfig it is possible that the * highlight windows effect activated and that the view is not displayed at all. As already mentioned * the TabBox does not handle any updating of the selected item. This has to be done by invoking * TabBoxHandler::setCurrentIndex(). Nevertheless the TabBoxHandler provides methods to query for the * model index or the next or previous item, for a cursor position or for a given item (that is * TabBoxClient or desktop). By invoking TabBoxHandler::hide() the view, the * optional highlight windows effect are removed. The model is invalidated immediately. So if it is * necessary to retrieve the last selected item this has to be done before calling the hide method. * * The layout of the TabBox View and the items is completely customizable. Therefore TabBox provides * a widget LayoutConfig which includes a live preview (in kcmkwin/kwintabbox). The layout of items * can be defined by an xml document. That way the user is able to define own custom layouts. The view * itself is made up of two widgets: one to show the complete list and one to show only the selected * item. This way it is possible to have a view which shows for example a list containing only small * icons and nevertheless show the title of the currently selected client. */ namespace TabBox { class DesktopModel; class ClientModel; class TabBoxConfig; class TabBoxClient; class TabBoxHandlerPrivate; typedef QList< QWeakPointer< TabBoxClient > > TabBoxClientList; /** * This class is a wrapper around KWin Workspace. It is used for accessing the * required core methods from inside TabBox and has to be implemented in KWin core. * * @author Martin Gräßlin * @since 4.4 */ class TabBoxHandler : public QObject { Q_OBJECT public: TabBoxHandler(QObject *parent); virtual ~TabBoxHandler(); /** * @return The id of the active screen */ virtual int activeScreen() const = 0; /** * @return The current active TabBoxClient or NULL * if there is no active client. */ virtual QWeakPointer activeClient() const = 0; /** * @param client The client which is starting point to find the next client * @return The next TabBoxClient in focus chain */ virtual QWeakPointer nextClientFocusChain(TabBoxClient* client) const = 0; /** * This method is used by the ClientModel to find an entrance into the focus chain in case * there is no active Client. * * @return The first Client of the focus chain * @since 4.9.1 **/ virtual QWeakPointer firstClientFocusChain() const = 0; /** * Checks whether the given @p client is part of the focus chain at all. * This is useful to figure out whether the currently active Client can be used * as a starting point to construct the recently used list. * * In case the @p client is not in the focus chain it is recommended to use the * Client returned by @link firstClientFocusChain. * * The method accepts a @c null Client and in that case @c false is returned. * @param client The Client to check whether it is in the Focus Chain * @return @c true in case the Client is part of the focus chain, @c false otherwise. * @since 4.9.2 **/ virtual bool isInFocusChain(TabBoxClient* client) const = 0; /** * @param client The client whose desktop name should be retrieved * @return The desktop name of the given TabBoxClient. If the client is * on all desktops the name of current desktop will be returned. */ virtual QString desktopName(TabBoxClient* client) const = 0; /** * @param desktop The desktop whose name should be retrieved * @return The desktop name of given desktop */ virtual QString desktopName(int desktop) const = 0; /** * @return The number of current desktop */ virtual int currentDesktop() const = 0; /** * @return The number of virtual desktops */ virtual int numberOfDesktops() const = 0; /** * @param desktop The desktop which is the starting point to find the next desktop * @return The next desktop in the current focus chain. */ virtual int nextDesktopFocusChain(int desktop) const = 0; /** * whether KWin is currently compositing and it's related features (elevating) can be used */ virtual bool isKWinCompositing() const = 0; /** * De-/Elevate a client using the compositor (if enabled) */ virtual void elevateClient(TabBoxClient* c, QWindow *tabbox, bool elevate) const = 0; /** * Raise a client (w/o activating it) */ virtual void raiseClient(TabBoxClient* c) const = 0; /** * @param c The client to be restacked * @param under The client the other one will be placed below */ virtual void restack(TabBoxClient *c, TabBoxClient *under) = 0; /** * Toggle between ShadeHover and ShadeNormal - not shaded windows are unaffected * @param c The client to be shaded * @param b Whether to un- or shade */ virtual void shadeClient(TabBoxClient *c, bool b) const = 0; virtual void highlightWindows(TabBoxClient *window = nullptr, QWindow *controller = nullptr) = 0; /** * @return The current stacking order of TabBoxClients */ virtual TabBoxClientList stackingOrder() const = 0; /** * Determines if given client will be added to the list: *
    *
  • Depends on desktop
  • *
  • if the client wants to have tab focus.
  • *
  • The client won't be added if it has modal dialogs
  • *
  • In that case the modal dialog will be returned if it isn't already * included
  • *
  • Won't be added if it isn't on active screen when using separate * screen focus
  • *
* @param client The client to be checked for inclusion * @param desktop The desktop the client should be on. This is irrelevant if allDesktops is set * @param allDesktops Add clients from all desktops or only from current * @return The client to be included in the list or NULL if it isn't to be included */ virtual QWeakPointer clientToAddToList(TabBoxClient* client, int desktop) const = 0; /** * @return The first desktop window in the stacking order. */ virtual QWeakPointer desktopClient() const = 0; /** * Activates the currently selected client and closes the TabBox. **/ virtual void activateAndClose() = 0; /** * @return The currently used TabBoxConfig */ const TabBoxConfig& config() const; /** * Call this method when you want to change the currently used TabBoxConfig. * It fires the signal configChanged. * @param config Updates the currently used TabBoxConfig to config */ void setConfig(const TabBoxConfig& config); /** * Call this method to show the TabBoxView. Depending on current * configuration this method might not do anything. * If highlight windows effect is to be used it will be activated. * Highlight windows and outline are not shown if * TabBoxConfig::TabBoxMode is TabBoxConfig::DesktopTabBox. * @see TabBoxConfig::isShowTabBox * @see TabBoxConfig::isHighlightWindows */ void show(); /** * Hides the TabBoxView if shown. * Deactivates highlight windows effect if active. * @see show */ void hide(bool abort = false); /** * Sets the current model index in the view and updates * highlight windows if active. * @param index The current Model index */ void setCurrentIndex(const QModelIndex& index); /** * @returns the current index **/ const QModelIndex ¤tIndex() const; /** * Retrieves the next or previous item of the current item. * @param forward next or previous item * @return The next or previous item. If there is no matching item * the current item will be returned. */ QModelIndex nextPrev(bool forward) const; /** * Initializes the model based on the current config. * This method has to be invoked before showing the TabBox. * It can also be invoked when clients are added or removed. * In that case partialReset has to be true. * * @param partialReset Keep the currently selected item or regenerate everything */ void createModel(bool partialReset = false); /** * @param desktop The desktop whose index should be retrieved * @return The model index of given desktop. If TabBoxMode is not * TabBoxConfig::DesktopTabBox an invalid model index will be returned. */ QModelIndex desktopIndex(int desktop) const; /** * @return The current list of desktops. * If TabBoxMode is not TabBoxConfig::DesktopTabBox an empty list will * be returned. * @see DesktopModel::desktopList */ QList< int > desktopList() const; /** * @return The desktop for given model index. If the index is not valid * or TabBoxMode is not TabBoxConfig::DesktopTabBox -1 will be returned. * @see DesktopModel::desktopIndex */ int desktop(const QModelIndex& index) const; /** * Handles additional grabbed key events by the TabBox controller. * @param event The key event which has been grabbed */ virtual void grabbedKeyEvent(QKeyEvent* event) const; /** * @param pos The position to be tested in global coordinates * @return True if the view contains the point, otherwise false. */ bool containsPos(const QPoint& pos) const; /** * @param client The TabBoxClient whose index should be returned * @return Returns the ModelIndex of given TabBoxClient or an invalid ModelIndex * if the model does not contain the given TabBoxClient. * @see ClientModel::index */ QModelIndex index(QWeakPointer client) const; /** * @return Returns the current list of TabBoxClients. * If TabBoxMode is not TabBoxConfig::ClientTabBox an empty list will * be returned. * @see ClientModel::clientList */ TabBoxClientList clientList() const; /** * @param index The index of the client to be returned * @return Returns the TabBoxClient at given model index. If * the index is invalid, does not point to a Client or the list * is empty, NULL will be returned. */ TabBoxClient* client(const QModelIndex& index) const; /** * @return The first model index. That is the model index at position 0, 0. * It is valid, as desktop has at least one desktop and if there are no * clients an empty item is created. */ QModelIndex first() const; bool eventFilter(QObject *watcher, QEvent *event) override; + /** + * @returns whether the TabBox operates in a no modifier grab mode. + * In this mode a click on an item should directly accept and close the tabbox. + **/ + virtual bool noModifierGrab() const = 0; + Q_SIGNALS: /** * This signal is fired when the TabBoxConfig changes * @see setConfig */ void configChanged(); void selectedIndexChanged(); private Q_SLOTS: void initHighlightWindows(); private: friend class TabBoxHandlerPrivate; TabBoxHandlerPrivate* d; }; /** * This class is a wrapper around a KWin Client. It is used for accessing the * required client methods from inside TabBox and has to be implemented in KWin core. * * @author Martin Gräßlin * @since 4.4 */ class TabBoxClient { public: TabBoxClient(); virtual ~TabBoxClient(); /** * @return The caption of the client */ virtual QString caption() const = 0; /** * @param size Requested size of the icon * @return The icon of the client */ virtual QIcon icon() const = 0; /** * @return The window Id of the client */ virtual WId window() const = 0; /** * @return Minimized state of the client */ virtual bool isMinimized() const = 0; virtual int x() const = 0; virtual int y() const = 0; virtual int width() const = 0; virtual int height() const = 0; virtual bool isCloseable() const = 0; virtual void close() = 0; virtual bool isFirstInTabBox() const = 0; }; /** * Pointer to the global TabBoxHandler object. **/ extern TabBoxHandler* tabBox; } // namespace TabBox } // namespace KWin #endif // TABBOXHANDLER_H