diff --git a/CMakeLists.txt b/CMakeLists.txt index 215e69fb21..09d25dcf31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,6 @@ endif(${KDE_PLATFORM_PROFILE} STREQUAL "Desktop") if(KWIN_PLASMA_ACTIVE) set(KWIN_BUILD_DECORATIONS OFF) set(KWIN_BUILD_KCMS OFF) - set(KWIN_BUILD_TABBOX OFF) set(KWIN_BUILD_TILING OFF) set(KWIN_BUILD_DESKTOPCHANGEOSD OFF) set(KWIN_BUILD_SCREENEDGES OFF) @@ -257,6 +256,7 @@ install( FILES tabbox/qml/text.qml tabbox/qml/thumbnails.qml tabbox/qml/IconTabBox.qml + tabbox/qml/window_strip.qml DESTINATION ${DATA_INSTALL_DIR}/kwin/tabbox ) kde4_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/atoms.cpp b/atoms.cpp index 5b9a2de523..7dd4aece15 100644 --- a/atoms.cpp +++ b/atoms.cpp @@ -126,6 +126,9 @@ Atoms::Atoms() atoms[n] = &kde_net_wm_tab_group; names[n++] = (char*) "_KDE_NET_WM_TAB_GROUP"; + atoms[n] = &kde_first_in_window_list; + names[n++] = (char*) "_KDE_FIRST_IN_WINDOWLIST"; + assert(n <= max); XInternAtoms(display(), names, n, false, atoms_return); diff --git a/atoms.h b/atoms.h index 4f1ce0dab6..95e1bde9ee 100644 --- a/atoms.h +++ b/atoms.h @@ -64,6 +64,7 @@ public: Atom kde_net_wm_shadow; Atom net_wm_opaque_region; Atom kde_net_wm_tab_group; + Atom kde_first_in_window_list; }; diff --git a/client.cpp b/client.cpp index 8c00249884..83b3862c71 100644 --- a/client.cpp +++ b/client.cpp @@ -129,6 +129,7 @@ Client::Client(Workspace* ws) , demandAttentionKNotifyTimer(NULL) , m_responsibleForDecoPixmap(false) , paintRedirector(0) + , m_firstInTabBox(false) , electricMaximizing(false) , activitiesDefined(false) , needsSessionInteract(false) @@ -2408,6 +2409,24 @@ QRect Client::decorationRect() const } } +void Client::updateFirstInTabBox() +{ + // TODO: move into KWindowInfo + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + status = XGetWindowProperty(display(), window(), atoms->kde_first_in_window_list, 0, 1, false, atoms->kde_first_in_window_list, &type, &format, &nitems, &extra, &data); + if (status == Success && format == 32 && nitems == 1) { + setFirstInTabBox(true); + } else { + setFirstInTabBox(false); + } + if (data) + XFree(data); +} + } // namespace #include "client.moc" diff --git a/client.h b/client.h index bdebe444ea..08cddc97c1 100644 --- a/client.h +++ b/client.h @@ -426,6 +426,13 @@ public: TabBox::TabBoxClientImpl* tabBoxClient() const { return m_tabBoxClient; } + bool isFirstInTabBox() const { + return m_firstInTabBox; + } + void setFirstInTabBox(bool enable) { + m_firstInTabBox = enable; + } + void updateFirstInTabBox(); //sets whether the client should be treated as a SessionInteract window void setSessionInteract(bool needed); @@ -719,6 +726,7 @@ private: bool m_responsibleForDecoPixmap; PaintRedirector* paintRedirector; TabBox::TabBoxClientImpl* m_tabBoxClient; + bool m_firstInTabBox; bool electricMaximizing; QuickTileMode electricMode; diff --git a/events.cpp b/events.cpp index 26e013912d..b9add4e226 100644 --- a/events.cpp +++ b/events.cpp @@ -857,6 +857,8 @@ void Client::propertyNotifyEvent(XPropertyEvent* e) checkActivities(); else if (e->atom == atoms->kde_net_wm_block_compositing) updateCompositeBlocking(true); + else if (e->atom == atoms->kde_first_in_window_list) + updateFirstInTabBox(); break; } } diff --git a/manage.cpp b/manage.cpp index 1877a3d96b..59c7674cbe 100644 --- a/manage.cpp +++ b/manage.cpp @@ -142,6 +142,7 @@ bool Client::manage(Window w, bool isMapped) original_skip_taskbar = skip_taskbar = (info->state() & NET::SkipTaskbar) != 0; skip_pager = (info->state() & NET::SkipPager) != 0; + updateFirstInTabBox(); setupCompositing(); diff --git a/scene.cpp b/scene.cpp index 81ca9378a8..82935c2e90 100644 --- a/scene.cpp +++ b/scene.cpp @@ -419,8 +419,8 @@ void Scene::paintWindow(Window* w, int mask, QRegion region, WindowQuadList quad } else { thumbMask |= PAINT_WINDOW_TRANSLUCENT; } - if (x < wImpl->x() || x + size.width() > wImpl->x() + wImpl->width() || - y < wImpl->y() || y + size.height() > wImpl->y() + wImpl->height()) { + if (item->isClip() && (x < wImpl->x() || x + size.width() > wImpl->x() + wImpl->width() || + y < wImpl->y() || y + size.height() > wImpl->y() + wImpl->height())) { // don't render windows outside the containing window. // TODO: improve by spliting out the window quads which do not fit continue; diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index eea8c91726..4ca7fef80a 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -43,6 +43,7 @@ ClientModel::ClientModel(QObject* parent) roles[DesktopNameRole] = "desktopName"; roles[MinimizedRole] = "minimized"; roles[WIdRole] = "windowId"; + roles[CloseableRole] = "closeable"; setRoleNames(roles); } @@ -80,6 +81,8 @@ QVariant ClientModel::data(const QModelIndex& index, int role) const return qulonglong(m_clientList[ clientIndex ]->window()); case MinimizedRole: return m_clientList[ clientIndex ]->isMinimized(); + case CloseableRole: + return m_clientList[ clientIndex ]->isCloseable(); default: return QVariant(); } @@ -172,6 +175,7 @@ void ClientModel::createClientList(int desktop, bool partialReset) start = m_clientList.first(); m_clientList.clear(); + QList stickyClients; switch(tabBox->config().clientSwitchingMode()) { case TabBoxConfig::FocusChainSwitching: { @@ -187,6 +191,9 @@ void ClientModel::createClientList(int desktop, bool partialReset) m_clientList.prepend(add); } else m_clientList += add; + if (add->isFirstInTabBox()) { + stickyClients << add; + } } c = tabBox->nextClientFocusChain(c); @@ -211,6 +218,9 @@ void ClientModel::createClientList(int desktop, bool partialReset) m_clientList.prepend(add); } else m_clientList += add; + if (add->isFirstInTabBox()) { + stickyClients << add; + } } if (index >= stacking.size() - 1) { c = NULL; @@ -224,6 +234,10 @@ void ClientModel::createClientList(int desktop, bool partialReset) break; } } + foreach (TabBoxClient *c, stickyClients) { + m_clientList.removeAll(c); + m_clientList.prepend(c); + } if (tabBox->config().isShowDesktop()) { TabBoxClient* desktopClient = tabBox->desktopClient(); if (desktopClient) @@ -232,5 +246,24 @@ void ClientModel::createClientList(int desktop, bool partialReset) reset(); } +void ClientModel::close(int i) +{ + QModelIndex ind = index(i, 0); + if (!ind.isValid()) { + return; + } + m_clientList.at(i)->close(); +} + +void ClientModel::activate(int i) +{ + QModelIndex ind = index(i, 0); + if (!ind.isValid()) { + return; + } + tabBox->setCurrentIndex(ind); + tabBox->activateAndClose(); +} + } // namespace Tabbox } // namespace KWin diff --git a/tabbox/clientmodel.h b/tabbox/clientmodel.h index acf81dd0ea..450d66515c 100644 --- a/tabbox/clientmodel.h +++ b/tabbox/clientmodel.h @@ -46,6 +46,7 @@ namespace TabBox class ClientModel : public QAbstractItemModel { + Q_OBJECT public: enum { ClientRole = Qt::UserRole, ///< The TabBoxClient @@ -54,7 +55,8 @@ public: IconRole = Qt::UserRole + 3, // TODO: to be removed EmptyRole = Qt::UserRole + 4, ///< Indicates if the model contains TabBoxClients WIdRole = Qt::UserRole + 5, ///< The window ID of TabBoxClient - MinimizedRole = Qt::UserRole + 6 ///< TabBoxClient is minimized + MinimizedRole = Qt::UserRole + 6, ///< TabBoxClient is minimized + CloseableRole = Qt::UserRole + 7 ///< TabBoxClient can be closed }; ClientModel(QObject* parent = 0); ~ClientModel(); @@ -94,6 +96,14 @@ public: return m_clientList; } +public Q_SLOTS: + void close(int index); + /** + * Activates the client at @p index and closes the TabBox. + * @param index The row index + **/ + void activate(int index); + private: TabBoxClientList m_clientList; }; diff --git a/tabbox/declarative.cpp b/tabbox/declarative.cpp index f7a9c989df..81103cc687 100644 --- a/tabbox/declarative.cpp +++ b/tabbox/declarative.cpp @@ -27,6 +27,8 @@ along with this program. If not, see . #include #include #include +#include + // include KDE #include #include @@ -39,6 +41,7 @@ along with this program. If not, see . #include // KWin #include "thumbnailitem.h" +#include namespace KWin { @@ -104,10 +107,16 @@ DeclarativeView::DeclarativeView(QAbstractItemModel *model, QWidget *parent) , m_currentScreenGeometry() , m_frame(new Plasma::FrameSvg(this)) , m_currentLayout() + , m_cachedWidth(0) + , m_cachedHeight(0) { setAttribute(Qt::WA_TranslucentBackground); setWindowFlags(Qt::X11BypassWindowManagerHint); - setResizeMode(QDeclarativeView::SizeViewToRootObject); + if (tabBox->embedded()) { + setResizeMode(QDeclarativeView::SizeRootObjectToView); + } else { + setResizeMode(QDeclarativeView::SizeViewToRootObject); + } QPalette pal = palette(); pal.setColor(backgroundRole(), Qt::transparent); setPalette(pal); @@ -130,10 +139,14 @@ DeclarativeView::DeclarativeView(QAbstractItemModel *model, QWidget *parent) m_frame->setEnabledBorders(Plasma::FrameSvg::AllBorders); connect(tabBox, SIGNAL(configChanged()), SLOT(updateQmlSource())); + connect(tabBox, SIGNAL(embeddedChanged(bool)), SLOT(slotEmbeddedChanged(bool))); } void DeclarativeView::showEvent(QShowEvent *event) { + if (tabBox->embedded()) { + connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,uint)), SLOT(slotWindowChanged(WId, uint))); + } updateQmlSource(); m_currentScreenGeometry = Kephal::ScreenUtils::screenGeometry(tabBox->activeScreen()); rootObject()->setProperty("screenWidth", m_currentScreenGeometry.width()); @@ -154,10 +167,12 @@ void DeclarativeView::showEvent(QShowEvent *event) void DeclarativeView::resizeEvent(QResizeEvent *event) { m_frame->resizeFrame(event->size()); - if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) { + if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled() && !tabBox->embedded()) { // blur background Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame->mask()); Plasma::WindowEffects::overrideShadow(winId(), true); + } else if (tabBox->embedded()) { + Plasma::WindowEffects::enableBlurBehind(winId(), false); } else { // do not trim to mask with compositing enabled, otherwise shadows are cropped setMask(m_frame->mask()); @@ -165,13 +180,76 @@ void DeclarativeView::resizeEvent(QResizeEvent *event) QDeclarativeView::resizeEvent(event); } +void DeclarativeView::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + if (tabBox->embedded()) { + disconnect(KWindowSystem::self(), SIGNAL(windowChanged(WId,uint)), this, SLOT(slotWindowChanged(WId,uint))); + } +} + +bool DeclarativeView::x11Event(XEvent *e) +{ + if (tabBox->embedded() && + (e->type == ButtonPress || e->type == ButtonRelease || e->type == MotionNotify)) { + XEvent ev; + + memcpy(&ev, e, sizeof(ev)); + if (e->type == ButtonPress || e->type == ButtonRelease) { + ev.xbutton.x += m_relativePos.x(); + ev.xbutton.y += m_relativePos.y(); + ev.xbutton.window = tabBox->embedded(); + } else if (e->type == MotionNotify) { + ev.xmotion.x += m_relativePos.x(); + ev.xmotion.y += m_relativePos.y(); + ev.xmotion.window = tabBox->embedded(); + } + + XSendEvent( QX11Info::display(), tabBox->embedded(), False, NoEventMask, &ev ); + } + return QDeclarativeView::x11Event(e); +} + void DeclarativeView::slotUpdateGeometry() { - const int width = rootObject()->property("width").toInt(); - const int height = rootObject()->property("height").toInt(); - setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, - m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, - width, height); + const WId embeddedId = tabBox->embedded(); + if (embeddedId != 0) { + const KWindowInfo info = KWindowSystem::windowInfo(embeddedId, NET::WMGeometry); + const Qt::Alignment alignment = tabBox->embeddedAlignment(); + const QPoint offset = tabBox->embeddedOffset(); + int x = info.geometry().left(); + int y = info.geometry().top(); + int width = tabBox->embeddedSize().width(); + int height = tabBox->embeddedSize().height(); + if (alignment.testFlag(Qt::AlignLeft) || alignment.testFlag(Qt::AlignHCenter)) { + x += offset.x(); + } + if (alignment.testFlag(Qt::AlignRight)) { + x = x + info.geometry().width() - offset.x() - width; + } + if (alignment.testFlag(Qt::AlignHCenter)) { + width = info.geometry().width() - 2 * offset.x(); + } + if (alignment.testFlag(Qt::AlignTop) || alignment.testFlag(Qt::AlignVCenter)) { + y += offset.y(); + } + if (alignment.testFlag(Qt::AlignBottom)) { + y = y + info.geometry().height() - offset.y() - height; + } + if (alignment.testFlag(Qt::AlignVCenter)) { + height = info.geometry().height() - 2 * offset.y(); + } + setGeometry(QRect(x, y, width, height)); + + m_relativePos = QPoint(info.geometry().x(), info.geometry().x()); + } else { + const int width = rootObject()->property("width").toInt(); + const int height = rootObject()->property("height").toInt(); + setGeometry(m_currentScreenGeometry.x() + static_cast(m_currentScreenGeometry.width()) * 0.5 - static_cast(width) * 0.5, + m_currentScreenGeometry.y() + static_cast(m_currentScreenGeometry.height()) * 0.5 - static_cast(height) * 0.5, + width, height); + m_relativePos = pos(); + } } void DeclarativeView::setCurrentIndex(const QModelIndex &index) @@ -201,9 +279,9 @@ void DeclarativeView::currentIndexChanged(int row) tabBox->setCurrentIndex(m_model->index(row, 0)); } -void DeclarativeView::updateQmlSource() +void DeclarativeView::updateQmlSource(bool force) { - if (tabBox->config().layoutName() == m_currentLayout) { + if (!force && tabBox->config().layoutName() == m_currentLayout) { return; } m_currentLayout = tabBox->config().layoutName(); @@ -215,5 +293,32 @@ void DeclarativeView::updateQmlSource() rootObject()->setProperty("source", QUrl(file)); } +void DeclarativeView::slotEmbeddedChanged(bool enabled) +{ + if (enabled) { + // cache the width + setResizeMode(QDeclarativeView::SizeRootObjectToView); + m_cachedWidth = rootObject()->property("width").toInt(); + m_cachedHeight = rootObject()->property("height").toInt(); + } else { + setResizeMode(QDeclarativeView::SizeViewToRootObject); + if (m_cachedWidth != 0 && m_cachedHeight != 0) { + rootObject()->setProperty("width", m_cachedWidth); + rootObject()->setProperty("height", m_cachedHeight); + } + updateQmlSource(true); + } +} + +void DeclarativeView::slotWindowChanged(WId wId, unsigned int properties) +{ + if (wId != tabBox->embedded()) { + return; + } + if (properties & NET::WMGeometry) { + slotUpdateGeometry(); + } +} + } // namespace TabBox } // namespace KWin diff --git a/tabbox/declarative.h b/tabbox/declarative.h index 56740549e4..104e6cf79d 100644 --- a/tabbox/declarative.h +++ b/tabbox/declarative.h @@ -58,11 +58,17 @@ public: void setCurrentIndex(const QModelIndex &index); QModelIndex indexAt(const QPoint &pos) const; +protected: + virtual void hideEvent(QHideEvent *event); + virtual bool x11Event(XEvent *e); + public Q_SLOTS: void slotUpdateGeometry(); + void slotEmbeddedChanged(bool enabled); private Q_SLOTS: - void updateQmlSource(); + void updateQmlSource(bool force = false); void currentIndexChanged(int row); + void slotWindowChanged(WId wId, unsigned int properties); private: QAbstractItemModel *m_model; QRect m_currentScreenGeometry; @@ -71,6 +77,10 @@ private: */ Plasma::FrameSvg* m_frame; QString m_currentLayout; + int m_cachedWidth; + int m_cachedHeight; + //relative position to the embedding window + QPoint m_relativePos; }; } // namespace TabBox diff --git a/tabbox/qml/window_strip.qml b/tabbox/qml/window_strip.qml new file mode 100644 index 0000000000..b63a138b90 --- /dev/null +++ b/tabbox/qml/window_strip.qml @@ -0,0 +1,145 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 Martin Gräßlin +Copyright (C) 2011 Marco Martin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.plasma.components 0.1 as PlasmaComponents +import org.kde.plasma.mobilecomponents 0.1 as MobileComponents +import org.kde.qtextracomponents 0.1 +import org.kde.kwin 0.1 as KWin + +Item { + id: thumbnailTabBox + property int screenWidth + property bool canStretchX: false + property bool canStretchY: false + width: screenWidth + height: 150 + clip: true + + + function setModel(model) { + thumbnailListView.model = model; + } + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + PlasmaCore.Theme { + id: theme + } + PlasmaCore.Svg { + id: iconsSvg + imagePath: "widgets/configuration-icons" + } + + ListView { + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return thumbnailListView.indexAt(pos.x, pos.y); + } + signal currentIndexChanged(int index) + id: thumbnailListView + objectName: "listView" + orientation: ListView.Horizontal + height: parent.height + spacing: 10 + anchors.fill: parent + clip: true + delegate: Item { + id: delegateItem + width: thumbnailListView.height * 1.6 + 48 + height: thumbnailListView.height + KWin.ThumbnailItem { + id: thumbnailItem + wId: windowId + width: parent.width - closeButtonContainer.width - 20 + height: thumbnailListView.height - 40 + clip: false + anchors.centerIn: parent + MouseArea { + anchors.fill: parent + onClicked: { + thumbnailListView.currentIndex = index; + thumbnailListView.currentIndexChanged(index); + } + } + } + PlasmaComponents.Label { + id: windowTitle + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter; + anchors.topMargin: 4 + + text: caption + elide: Text.ElideRight + color: theme.textColor + width: parent.width - 40 + horizontalAlignment: Text.AlignHCenter + } + PlasmaCore.FrameSvgItem { + id: closeButtonContainer + imagePath: "widgets/button" + prefix: "shadow" + width: closeButton.width + margins.left + margins.right + height: closeButton.height + margins.top + margins.bottom + visible: closeable + anchors { + top: parent.top + right: parent.right + topMargin: 32 + } + + PlasmaCore.FrameSvgItem { + id: closeButton + imagePath: "widgets/button" + prefix: "normal" + //a bit more left margin + width: closeButtonSvg.width + margins.left + margins.right + 16 + height: closeButtonSvg.height + margins.top + margins.bottom + x: parent.margins.left + y: parent.margins.top + + MobileComponents.ActionButton { + id: closeButtonSvg + svg: iconsSvg + iconSize: 22 + backgroundVisible: false + elementId: "close" + + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: parent.margins.right + } + + onClicked: thumbnailListView.model.close(index) + } + } + } + } + } +} diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 9cc7935875..ff4084cace 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -34,6 +34,7 @@ along with this program. If not, see . // Qt #include #include +#include // KDE #include #include @@ -213,6 +214,10 @@ QVector< Window > TabBoxHandlerImpl::outlineWindowIds() const return Workspace::self()->outline()->windowIds(); } +void TabBoxHandlerImpl::activateAndClose() +{ + m_tabBox->accept(); +} /********************************************************* * TabBoxClientImpl @@ -270,6 +275,20 @@ 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 @@ -279,6 +298,7 @@ TabBox::TabBox(QObject *parent) , m_displayRefcount(0) , m_desktopGrab(false) , m_tabGrab(false) + , m_noModifierGrab(false) , m_forcedGlobalMouseGrab(false) , m_ready(false) { @@ -310,14 +330,17 @@ TabBox::TabBox(QObject *parent) m_desktopListConfig.setLayout(TabBoxConfig::VerticalLayout); m_tabBox = new TabBoxHandlerImpl(this); connect(m_tabBox, SIGNAL(ready()), SLOT(handlerReady())); + connect(m_tabBox, SIGNAL(selectedIndexChanged()), SIGNAL(itemSelected())); m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); connect(Workspace::self(), SIGNAL(configChanged()), this, SLOT(reconfigure())); + QDBusConnection::sessionBus().registerObject("/TabBox", this, QDBusConnection::ExportScriptableContents); } TabBox::~TabBox() { + QDBusConnection::sessionBus().unregisterObject("/TabBox"); } void TabBox::handlerReady() @@ -659,6 +682,11 @@ void TabBox::grabbedKeyEvent(QKeyEvent* event) // tabbox has been replaced, check effects return; } + if (m_noModifierGrab) { + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) { + accept(); + } + } setCurrentIndex(m_tabBox->grabbedKeyEvent(event)); } @@ -913,11 +941,48 @@ void TabBox::modalActionsSwitch(bool enabled) action->setEnabled(enabled); } +void TabBox::open(bool modal) +{ + if (isDisplayed()) { + return; + } + if (modal) { + if (!establishTabBoxGrab()) { + return; + } + m_tabGrab = true; + } else { + m_tabGrab = false; + } + m_noModifierGrab = !modal; + setMode(TabBoxWindowsMode); + reset(); + show(); +} + +void TabBox::openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment) +{ + if (isDisplayed()) { + return; + } + m_tabGrab = false; + m_noModifierGrab = true; + tabBox->setEmbedded(static_cast(wid)); + tabBox->setEmbeddedOffset(offset); + tabBox->setEmbeddedSize(size); + tabBox->setEmbeddedAlignment(static_cast(horizontalAlignment) | static_cast(verticalAlignment)); + setMode(TabBoxWindowsMode); + reset(); + show(); +} + bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) { if (!establishTabBoxGrab()) return false; m_tabGrab = true; + m_noModifierGrab = false; + tabBox->resetEmbedded(); modalActionsSwitch(false); setMode(mode); reset(); @@ -929,6 +994,7 @@ bool TabBox::startWalkThroughDesktops(TabBoxMode mode) if (!establishTabBoxGrab()) return false; m_desktopGrab = true; + m_noModifierGrab = false; modalActionsSwitch(false); setMode(mode); reset(); @@ -1091,11 +1157,32 @@ void TabBox::keyPress(int keyQt) void TabBox::close(bool abort) { - removeTabBoxGrab(); + if (isGrabbed()) { + removeTabBoxGrab(); + } hide(abort); modalActionsSwitch(true); m_tabGrab = false; m_desktopGrab = false; + m_noModifierGrab = false; +} + +void TabBox::accept() +{ + Client* c = currentClient(); + close(); + if (c) { + Workspace::self()->activateClient(c); + if (c->isShade() && options->shadeHover) + c->setShade(ShadeActivated); + if (c->isDesktop()) + Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); + } +} + +void TabBox::reject() +{ + close(true); } /*! @@ -1103,6 +1190,9 @@ void TabBox::close(bool abort) */ void TabBox::keyRelease(const XKeyEvent& ev) { + if (m_noModifierGrab) { + return; + } unsigned int mk = ev.state & (KKeyServer::modXShift() | KKeyServer::modXCtrl() | @@ -1136,16 +1226,8 @@ void TabBox::keyRelease(const XKeyEvent& ev) return; if (m_tabGrab) { bool old_control_grab = m_desktopGrab; - Client* c = currentClient(); - close(); + accept(); m_desktopGrab = old_control_grab; - if (c) { - Workspace::self()->activateClient(c); - if (c->isShade() && options->shadeHover) - c->setShade(ShadeActivated); - if (c->isDesktop()) - Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); - } } if (m_desktopGrab) { bool old_tab_grab = m_tabGrab; diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 6ab07d69d3..6d4e2b2479 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -61,6 +61,7 @@ public: virtual void hideOutline(); virtual void showOutline(const QRect &outline); virtual QVector< Window > outlineWindowIds() const; + virtual void activateAndClose(); private: TabBox* m_tabBox; @@ -80,6 +81,9 @@ public: virtual int y() const; virtual int width() const; virtual int height() const; + virtual bool isCloseable() const; + virtual void close(); + virtual bool isFirstInTabBox() const; Client* client() const { return m_client; @@ -95,6 +99,7 @@ private: class TabBox : public QObject { Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin") public: TabBox(QObject *parent = NULL); ~TabBox(); @@ -160,12 +165,36 @@ public: int previousDesktopFocusChain(int iDesktop) const; int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; - void close(bool abort = false); void keyPress(int key); void keyRelease(const XKeyEvent& ev); public slots: void show(); + /** + * Only for DBus Interface to start primary KDE Walk through windows. + * @param modal Whether the TabBox should grab keyboard and mouse, that is go into modal + * mode or whether the TabBox is controlled externally (e.g. through an effect). + **/ + Q_SCRIPTABLE void open(bool modal = true); + /** + * Opens the TabBox view embedded on a different window. This implies non-modal mode. + * The geometry of the TabBox is determined by offset, size and the alignment flags. + * If the alignment flags are set to center the view scales with the container. That is if + * the window where the TabBox is embedded onto resizes, the TabBox resizes, too. + * The alignment in combination with the offset determines to what border the TabBox is snapped. + * E.g. if horizontal alignment is right the offset is interpreted as the offset between right + * corner of TabBox view and the container view. When the container changes its geometry this + * offset is kept. So the offset on the left side would increase. + * @param wid The window Id the TabBox should be embedded onto + * @param offset The offset to one of the size borders + * @param size The size of the TabBox. To use the same size as the container, set alignment to center + * @param horizontalAlignment Either Qt::AlignLeft, Qt::AlignHCenter or Qt::AlignRight + * @param verticalAlignment Either Qt::AlignTop, Qt::AlignVCenter or Qt::AlignBottom + **/ + Q_SCRIPTABLE void openEmbedded(qulonglong wid, QPoint offset, QSize size, int horizontalAlignment, int verticalAlignment); + Q_SCRIPTABLE void close(bool abort = false); + Q_SCRIPTABLE void accept(); + Q_SCRIPTABLE void reject(); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); @@ -190,7 +219,8 @@ public slots: signals: void tabBoxAdded(int); - void tabBoxClosed(); + Q_SCRIPTABLE void tabBoxClosed(); + Q_SCRIPTABLE void itemSelected(); void tabBoxUpdated(); void tabBoxKeyEvent(QKeyEvent*); @@ -235,6 +265,8 @@ private: bool m_isShown; bool m_desktopGrab; bool m_tabGrab; + // true if tabbox is in modal mode which does not require holding a modifier + bool m_noModifierGrab; KShortcut m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse; KShortcut m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse; KShortcut m_cutWalkThroughWindows, m_cutWalkThroughWindowsReverse; diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index a03dd5fe02..00c0343f0b 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -89,11 +89,18 @@ public: bool isShown; QMap< QString, ItemLayoutConfig > tabBoxLayouts; TabBoxClient *lastRaisedClient, *lastRaisedClientSucc; + WId m_embedded; + QPoint m_embeddedOffset; + QSize m_embeddedSize; + Qt::Alignment m_embeddedAlignment; }; TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : view(NULL) , m_declarativeView(NULL) + , m_embedded(0) + , m_embeddedOffset(QPoint(0, 0)) + , m_embeddedSize(QSize(0, 0)) { this->q = q; isShown = false; @@ -552,6 +559,7 @@ void TabBoxHandler::setCurrentIndex(const QModelIndex& index) d->updateHighlightWindows(); } } + emit selectedIndexChanged(); } const QModelIndex& TabBoxHandler::currentIndex() const @@ -586,11 +594,13 @@ QModelIndex TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const if (column >= model->columnCount()) column = 0; break; + case Qt::Key_Backtab: case Qt::Key_Up: row--; if (row < 0) row = model->rowCount() - 1; break; + case Qt::Key_Tab: case Qt::Key_Down: row++; if (row >= model->rowCount()) @@ -696,6 +706,58 @@ QWidget* TabBoxHandler::tabBoxView() const return d->view; } +WId TabBoxHandler::embedded() const +{ + return d->m_embedded; +} + +void TabBoxHandler::setEmbedded(WId wid) +{ + d->m_embedded = wid; + emit embeddedChanged(wid != 0); +} + +void TabBoxHandler::setEmbeddedOffset(const QPoint &offset) +{ + d->m_embeddedOffset = offset; +} + +void TabBoxHandler::setEmbeddedSize(const QSize &size) +{ + d->m_embeddedSize = size; +} + +const QPoint &TabBoxHandler::embeddedOffset() const +{ + return d->m_embeddedOffset; +} + +const QSize &TabBoxHandler::embeddedSize() const +{ + return d->m_embeddedSize; +} + +Qt::Alignment TabBoxHandler::embeddedAlignment() const +{ + return d->m_embeddedAlignment; +} + +void TabBoxHandler::setEmbeddedAlignment(Qt::Alignment alignment) +{ + d->m_embeddedAlignment = alignment; +} + +void TabBoxHandler::resetEmbedded() +{ + if (d->m_embedded == 0) { + return; + } + d->m_embedded = 0; + d->m_embeddedOffset = QPoint(0, 0); + d->m_embeddedSize = QSize(0, 0); + emit embeddedChanged(false); +} + TabBoxHandler* tabBox = 0; TabBoxClient::TabBoxClient() diff --git a/tabbox/tabboxhandler.h b/tabbox/tabboxhandler.h index bf9fecdee9..a20801b2db 100644 --- a/tabbox/tabboxhandler.h +++ b/tabbox/tabboxhandler.h @@ -173,6 +173,10 @@ public: * @return The first desktop window in the stacking order. */ virtual TabBoxClient* desktopClient() const = 0; + /** + * Activates the currently selected client and closes the TabBox. + **/ + virtual void activateAndClose() = 0; /** * @return The currently used TabBoxConfig @@ -311,6 +315,16 @@ public: */ QModelIndex first() const; + void setEmbedded(WId wid); + WId embedded() const; + void setEmbeddedOffset(const QPoint &offset); + const QPoint &embeddedOffset() const; + void setEmbeddedSize(const QSize &size); + const QSize &embeddedSize() const; + void setEmbeddedAlignment(Qt::Alignment alignment); + Qt::Alignment embeddedAlignment() const; + void resetEmbedded(); + /** * @return The tabBoxView Widget */ @@ -344,6 +358,8 @@ signals: */ void configChanged(); void ready(); + void embeddedChanged(bool enabled); + void selectedIndexChanged(); private: friend class TabBoxHandlerPrivate; @@ -384,6 +400,9 @@ public: 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; }; /** diff --git a/thumbnailitem.cpp b/thumbnailitem.cpp index 8c209833f0..73441a059f 100644 --- a/thumbnailitem.cpp +++ b/thumbnailitem.cpp @@ -35,6 +35,7 @@ namespace KWin ThumbnailItem::ThumbnailItem(QDeclarativeItem* parent) : QDeclarativeItem(parent) , m_wId(0) + , m_clip(true) , m_parent(QWeakPointer()) { setFlags(flags() & ~QGraphicsItem::ItemHasNoContents); @@ -81,6 +82,12 @@ void ThumbnailItem::setWId(qulonglong wId) emit wIdChanged(wId); } +void ThumbnailItem::setClip(bool clip) +{ + m_clip = clip; + emit clipChanged(clip); +} + void ThumbnailItem::effectWindowAdded() { // the window might be added before the EffectWindow is created diff --git a/thumbnailitem.h b/thumbnailitem.h index 2ff354672f..8d39f5fc26 100644 --- a/thumbnailitem.h +++ b/thumbnailitem.h @@ -34,6 +34,7 @@ class ThumbnailItem : public QDeclarativeItem { Q_OBJECT Q_PROPERTY(qulonglong wId READ wId WRITE setWId NOTIFY wIdChanged SCRIPTABLE true) + Q_PROPERTY(bool clip READ isClip WRITE setClip NOTIFY clipChanged SCRIPTABLE true) public: ThumbnailItem(QDeclarativeItem *parent = 0); virtual ~ThumbnailItem(); @@ -42,15 +43,21 @@ public: return m_wId; } void setWId(qulonglong wId); + bool isClip() const { + return m_clip; + } + void setClip(bool clip); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); Q_SIGNALS: void wIdChanged(qulonglong wid); + void clipChanged(bool clipped); private Q_SLOTS: void init(); void effectWindowAdded(); private: void findParentEffectWindow(); qulonglong m_wId; + bool m_clip; QWeakPointer m_parent; }; diff --git a/workspace.cpp b/workspace.cpp index 34c3340a65..f339422b7d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -638,7 +638,7 @@ void Workspace::addClient(Client* c, allowed_t) updateToolWindows(true); checkNonExistentClients(); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed()) + if (tabBox()->isDisplayed()) tab_box->reset(true); #endif } @@ -672,7 +672,7 @@ void Workspace::removeClient(Client* c, allowed_t) Notify::raise(Notify::Delete); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed() && tabBox()->currentClient() == c) + if (tabBox()->isDisplayed() && tabBox()->currentClient() == c) tab_box->nextPrev(true); #endif @@ -708,7 +708,7 @@ void Workspace::removeClient(Client* c, allowed_t) updateCompositeBlocking(); #ifdef KWIN_BUILD_TABBOX - if (tabBox()->isGrabbed()) + if (tabBox()->isDisplayed()) tab_box->reset(true); #endif