diff --git a/CMakeLists.txt b/CMakeLists.txt index bde6626ab3..24785cd6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ if(KWIN_BUILD_TABBOX) tabbox/tabbox.cpp tabbox/clientitemdelegate.cpp tabbox/clientmodel.cpp + tabbox/declarative.cpp tabbox/desktopitemdelegate.cpp tabbox/desktopmodel.cpp tabbox/itemlayoutconfig.cpp @@ -195,7 +196,7 @@ if(KWIN_BUILD_SCRIPTING) endif(KWIN_BUILD_SCRIPTING) if(KWIN_BUILD_TABBOX) - set(kwinLibs ${kwinLibs} ${QT_QTXML_LIBRARY}) + set(kwinLibs ${kwinLibs} ${QT_QTXML_LIBRARY} ${QT_QTDECLARATIVE_LIBRARY} kdeclarative) endif(KWIN_BUILD_TABBOX) kde4_add_kdeinit_executable( kwin ${kwin_KDEINIT_SRCS}) @@ -246,5 +247,14 @@ install( FILES kwin.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES kwin.notifyrc DESTINATION ${DATA_INSTALL_DIR}/kwin ) install( FILES org.kde.KWin.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} ) install( FILES tabbox/DefaultTabBoxLayouts.xml DESTINATION ${DATA_INSTALL_DIR}/kwin ) +install( FILES + tabbox/qml/informative.qml + tabbox/qml/big_icons.qml + tabbox/qml/compact.qml + tabbox/qml/small_icons.qml + tabbox/qml/tabbox.qml + tabbox/qml/text.qml + tabbox/qml/IconTabBox.qml + DESTINATION ${DATA_INSTALL_DIR}/kwin/tabbox ) kde4_install_icons( ${ICON_INSTALL_DIR} ) diff --git a/kcmkwin/kwintabbox/CMakeLists.txt b/kcmkwin/kwintabbox/CMakeLists.txt index a4b1db820e..826092f361 100644 --- a/kcmkwin/kwintabbox/CMakeLists.txt +++ b/kcmkwin/kwintabbox/CMakeLists.txt @@ -8,6 +8,7 @@ set(kcm_kwintabbox_PART_SRCS previewhandlerimpl.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/clientitemdelegate.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/clientmodel.cpp + ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/declarative.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/desktopitemdelegate.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/desktopmodel.cpp ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/tabbox/itemlayoutconfig.cpp @@ -20,7 +21,7 @@ kde4_add_ui_files( kcm_kwintabbox_PART_SRCS layoutconfig.ui ) kde4_add_plugin(kcm_kwintabbox ${kcm_kwintabbox_PART_SRCS}) -target_link_libraries(kcm_kwintabbox ${KDE4_KDEUI_LIBS} ${KDE4_KCMUTILS_LIBS} ${KDE4_PLASMA_LIBS} ${X11_LIBRARIES} ${QT_QTXML_LIBRARY} kephal ) +target_link_libraries(kcm_kwintabbox ${KDE4_KDEUI_LIBS} ${KDE4_KCMUTILS_LIBS} ${KDE4_PLASMA_LIBS} ${X11_LIBRARIES} ${QT_QTXML_LIBRARY} kephal ${QT_QTDECLARATIVE_LIBRARY} kdeclarative ) install(TARGETS kcm_kwintabbox DESTINATION ${PLUGIN_INSTALL_DIR} ) diff --git a/tabbox/clientmodel.cpp b/tabbox/clientmodel.cpp index 8a24a5d1b7..d7f10541d2 100644 --- a/tabbox/clientmodel.cpp +++ b/tabbox/clientmodel.cpp @@ -38,6 +38,11 @@ namespace TabBox ClientModel::ClientModel(QObject* parent) : QAbstractItemModel(parent) { + QHash roles; + roles[CaptionRole] = "caption"; + roles[DesktopNameRole] = "desktopName"; + roles[MinimizedRole] = "minimized"; + setRoleNames(roles); } ClientModel::~ClientModel() @@ -79,6 +84,17 @@ QVariant ClientModel::data(const QModelIndex& index, int role) const } } +QString ClientModel::longestCaption() const +{ + QString caption; + foreach (TabBoxClient *client, m_clientList) { + if (client->caption().size() > caption.size()) { + caption = client->caption(); + } + } + return caption; +} + int ClientModel::columnCount(const QModelIndex& parent) const { Q_UNUSED(parent) diff --git a/tabbox/clientmodel.h b/tabbox/clientmodel.h index f3352dff37..acf81dd0ea 100644 --- a/tabbox/clientmodel.h +++ b/tabbox/clientmodel.h @@ -63,6 +63,7 @@ public: virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex& child) const; virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + QString longestCaption() const; /** * @param client The TabBoxClient whose index should be returned diff --git a/tabbox/declarative.cpp b/tabbox/declarative.cpp new file mode 100644 index 0000000000..1567497137 --- /dev/null +++ b/tabbox/declarative.cpp @@ -0,0 +1,195 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 "declarative.h" +#include "tabboxhandler.h" +#include "clientmodel.h" +// Qt +#include +#include +#include +#include +#include +// include KDE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KWin +{ +namespace TabBox +{ + +ImageProvider::ImageProvider(QAbstractItemModel *model) + : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap) + , m_model(model) +{ +} + +QPixmap ImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + bool ok = false; + QStringList parts = id.split('/'); + const int row = parts.first().toInt(&ok); + if (!ok) { + return QDeclarativeImageProvider::requestPixmap(id, size, requestedSize); + } + const QModelIndex index = m_model->index(row, 0); + if (!index.isValid()) { + return QDeclarativeImageProvider::requestPixmap(id, size, requestedSize); + } + TabBoxClient* client = static_cast< TabBoxClient* >(index.model()->data(index, ClientModel::ClientRole).value()); + + QSize s(32, 32); + if (requestedSize.isValid()) { + s = requestedSize; + } + *size = s; + QPixmap icon = client->icon(s); + if (parts.size() > 2) { + KIconEffect *effect = KIconLoader::global()->iconEffect(); + KIconLoader::States state = KIconLoader::DefaultState; + if (parts.at(2) == QLatin1String("selected")) { + state = KIconLoader::ActiveState; + } else if (parts.at(2) == QLatin1String("disabled")) { + state = KIconLoader::DisabledState; + } + icon = effect->apply(icon, KIconLoader::Desktop, state); + } + return icon; +} + +DeclarativeView::DeclarativeView(QAbstractItemModel *model, QWidget *parent) + : QDeclarativeView(parent) + , m_model(model) + , m_currentScreenGeometry() + , m_frame(new Plasma::FrameSvg(this)) + , m_currentLayout() +{ + setAttribute(Qt::WA_TranslucentBackground); + setWindowFlags(Qt::X11BypassWindowManagerHint); + setResizeMode(QDeclarativeView::SizeViewToRootObject); + QPalette pal = palette(); + pal.setColor(backgroundRole(), Qt::transparent); + setPalette(pal); + foreach (const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) { + engine()->addImportPath(importPath); + } + engine()->addImageProvider(QLatin1String("client"), new ImageProvider(model)); + KDeclarative kdeclarative; + kdeclarative.setDeclarativeEngine(engine()); + kdeclarative.initialize(); + kdeclarative.setupBindings(); + rootContext()->setContextProperty("clientModel", model); + updateQmlSource(); + + // FrameSvg + m_frame->setImagePath("dialogs/background"); + m_frame->setCacheAllRenderedFrames(true); + m_frame->setEnabledBorders(Plasma::FrameSvg::AllBorders); + + connect(tabBox, SIGNAL(configChanged()), SLOT(updateQmlSource())); +} + +void DeclarativeView::showEvent(QShowEvent *event) +{ + m_currentScreenGeometry = Kephal::ScreenUtils::screenGeometry(tabBox->activeScreen()); + rootObject()->setProperty("screenWidth", m_currentScreenGeometry.width()); + rootObject()->setProperty("screenHeight", m_currentScreenGeometry.height()); + rootObject()->setProperty("allDesktops", tabBox->config().tabBoxMode() == TabBoxConfig::ClientTabBox && + ((tabBox->config().clientListMode() == TabBoxConfig::AllDesktopsClientList) || + (tabBox->config().clientListMode() == TabBoxConfig::AllDesktopsApplicationList))); + rootObject()->setProperty("longestCaption", static_cast(m_model)->longestCaption()); + + if (QObject *item = rootObject()->findChild("listView")) { + item->setProperty("currentIndex", tabBox->first().row()); + } + slotUpdateGeometry(); + QGraphicsView::showEvent(event); +} + +void DeclarativeView::resizeEvent(QResizeEvent *event) +{ + m_frame->resizeFrame(event->size()); + if (Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) { + // blur background + Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame->mask()); + Plasma::WindowEffects::overrideShadow(winId(), true); + } else { + // do not trim to mask with compositing enabled, otherwise shadows are cropped + setMask(m_frame->mask()); + } + QDeclarativeView::resizeEvent(event); +} + +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); +} + +void DeclarativeView::setCurrentIndex(const QModelIndex &index) +{ + if (QObject *item = rootObject()->findChild("listView")) { + item->setProperty("currentIndex", index.row()); + } +} + +QModelIndex DeclarativeView::indexAt(const QPoint &pos) const +{ + if (QObject *item = rootObject()->findChild("listView")) { + QVariant returnedValue; + QVariant xPos(pos.x()); + QVariant yPos(pos.y()); + QMetaObject::invokeMethod(item, "indexAtMousePos", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, QVariant(pos))); + if (!returnedValue.canConvert()) { + return QModelIndex(); + } + return m_model->index(returnedValue.toInt(), 0); + } + return QModelIndex(); +} + +void DeclarativeView::updateQmlSource() +{ + if (tabBox->config().layoutName() == m_currentLayout) { + return; + } + m_currentLayout = tabBox->config().layoutName(); + setSource(QUrl(KStandardDirs::locate("data", "kwin/tabbox/tabbox.qml"))); + QString file = KStandardDirs::locate("data", "kwin/tabbox/" + m_currentLayout.toLower().replace(' ', '_') + ".qml"); + if (file.isNull()) { + // fallback to default + file = KStandardDirs::locate("data", "kwin/tabbox/informative.qml"); + } + rootObject()->setProperty("source", QUrl(file)); +} + +} // namespace TabBox +} // namespace KWin diff --git a/tabbox/declarative.h b/tabbox/declarative.h new file mode 100644 index 0000000000..b9befb5883 --- /dev/null +++ b/tabbox/declarative.h @@ -0,0 +1,77 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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_DECLARATIVE_H +#define KWIN_TABBOX_DECLARATIVE_H +// includes +#include +#include + +// forward declaration +class QAbstractItemModel; +class QModelIndex; +class QPos; + +namespace Plasma +{ +class FrameSvg; +} + +namespace KWin +{ +namespace TabBox +{ + +class ImageProvider : public QDeclarativeImageProvider +{ +public: + ImageProvider(QAbstractItemModel *model); + virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize); + +private: + QAbstractItemModel *m_model; +}; + +class DeclarativeView : public QDeclarativeView +{ + Q_OBJECT +public: + DeclarativeView(QAbstractItemModel *model, QWidget *parent = NULL); + virtual void showEvent(QShowEvent *event); + virtual void resizeEvent(QResizeEvent *event); + void setCurrentIndex(const QModelIndex &index); + QModelIndex indexAt(const QPoint &pos) const; + +public Q_SLOTS: + void slotUpdateGeometry(); +private Q_SLOTS: + void updateQmlSource(); +private: + QAbstractItemModel *m_model; + QRect m_currentScreenGeometry; + /** + * Background Frame required for setting the blur mask + */ + Plasma::FrameSvg* m_frame; + QString m_currentLayout; +}; + +} // namespace TabBox +} // namespace KWin +#endif diff --git a/tabbox/qml/IconTabBox.qml b/tabbox/qml/IconTabBox.qml new file mode 100644 index 0000000000..9f5a2c85e8 --- /dev/null +++ b/tabbox/qml/IconTabBox.qml @@ -0,0 +1,106 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: iconsTabBox + property int iconSize + property int imagePathPrefix: (new Date()).getTime() + property alias count: iconsListView.count + property alias margins: hoverItem.margins + + + function setModel(model) { + iconsListView.model = model; + iconsListView.imageId++; + } + + function modelChanged() { + iconsListView.imageId++; + } + + PlasmaCore.Theme { + id: theme + } + + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + // delegate + Component { + id: listDelegate + Item { + id: delegateItem + width: iconSize + hoverItem.margins.left + hoverItem.margins.right + height: iconSize + hoverItem.margins.top + hoverItem.margins.bottom + Image { + id: iconItem + source: "image://client/" + index + "/" + iconsTabBox.imagePathPrefix + "-" + iconsListView.imageId + (index == iconsListView.currentIndex ? "/selected" : "") + sourceSize { + width: iconSize + height: iconSize + } + anchors { + fill: parent + leftMargin: hoverItem.margins.left + rightMargin: hoverItem.margins.right + topMargin: hoverItem.margins.top + bottomMargin: hoverItem.margins.bottom + } + } + } + } + ListView { + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return iconsListView.indexAt(pos.x, pos.y); + } + id: iconsListView + objectName: "listView" + orientation: ListView.Horizontal + // used for image provider URL to trick Qt into reloading icons when the model changes + property int imageId: 0 + anchors { + fill: parent + } + clip: true + delegate: listDelegate + highlight: PlasmaCore.FrameSvgItem { + id: highlightItem + imagePath: "widgets/viewitem" + prefix: "hover" + width: iconSize + margins.left + margins.right + height: iconSize + margins.top + margins.bottom + } + MouseArea { + anchors.fill: parent + onClicked: iconsListView.currentIndex = iconsListView.indexAt(mouse.x, mouse.y) + } + } +} diff --git a/tabbox/qml/big_icons.qml b/tabbox/qml/big_icons.qml new file mode 100644 index 0000000000..4d8162d5d1 --- /dev/null +++ b/tabbox/qml/big_icons.qml @@ -0,0 +1,58 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: bigIconsTabBox + property int screenWidth : 0 + property int screenHeight : 0 + property int imagePathPrefix: (new Date()).getTime() + width: Math.min(Math.max(screenWidth * 0.3, (icons.iconSize + icons.margins.left + icons.margins.right) * icons.count + background.margins.left + background.margins.bottom), screenWidth * 0.9) + height: Math.min(Math.max(screenHeight * 0.05, icons.iconSize + icons.margins.top + icons.margins.bottom + background.margins.top + background.margins.bottom), screenHeight * 0.5) + + + function setModel(model) { + icons.setModel(model); + } + + function modelChanged() { + icons.modelChanged(); + } + + PlasmaCore.FrameSvgItem { + id: background + anchors.fill: parent + imagePath: "dialogs/background" + } + + IconTabBox { + id: icons + iconSize: 64 + anchors { + fill: parent + topMargin: background.margins.top + rightMargin: background.margins.right + bottomMargin: background.margins.bottom + leftMargin: background.margins.left + } + } +} diff --git a/tabbox/qml/compact.qml b/tabbox/qml/compact.qml new file mode 100644 index 0000000000..ee2dee67ea --- /dev/null +++ b/tabbox/qml/compact.qml @@ -0,0 +1,187 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: compactTabBox + property int screenWidth : 0 + property int screenHeight : 0 + property string longestCaption: "" + property int imagePathPrefix: (new Date()).getTime() + width: Math.min(Math.max(screenWidth * 0.2, compactListView.maxRowWidth), screenWidth * 0.8) + height: Math.min(Math.max(screenHeight * 0.2, compactListView.rowHeight * compactListView.count + background.margins.top + background.margins.bottom), screenHeight * 0.8) + + property int textMargin: 2 + + onLongestCaptionChanged: { + compactListView.maxRowWidth = compactListView.calculateMaxRowWidth(); + } + + function setModel(model) { + compactListView.model = model; + compactListView.maxRowWidth = compactListView.calculateMaxRowWidth(); + compactListView.imageId++; + } + + function modelChanged() { + compactListView.imageId++; + } + + /** + * Returns the caption with adjustments for minimized items. + * @param caption the original caption + * @param mimized whether the item is minimized + * @return Caption adjusted for minimized state + **/ + function itemCaption(caption, minimized) { + var text = caption; + if (minimized) { + text = "(" + text + ")"; + } + return text; + } + + PlasmaCore.Theme { + id: theme + } + + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + PlasmaCore.FrameSvgItem { + id: background + anchors.fill: parent + imagePath: "dialogs/background" + } + + // delegate + Component { + id: listDelegate + Item { + id: delegateItem + width: compactListView.width + height: compactListView.rowHeight + Image { + id: iconItem + source: "image://client/" + index + "/" + compactTabBox.imagePathPrefix + "-" + compactListView.imageId + (index == compactListView.currentIndex ? "/selected" : "/disabled") + width: 16 + height: 16 + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: hoverItem.margins.left + } + } + Text { + id: captionItem + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignBottom + text: itemCaption(caption, minimized) + font.bold: true + font.italic: minimized + color: theme.textColor + elide: Text.ElideMiddle + anchors { + left: iconItem.right + right: parent.right + top: parent.top + bottom: parent.bottom + topMargin: hoverItem.margins.top + rightMargin: hoverItem.margins.right + bottomMargin: hoverItem.margins.bottom + leftMargin: 2 * compactTabBox.textMargin + } + } + } + } + ListView { + function calculateMaxRowWidth() { + var width = 0; + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "' + itemCaption(compactTabBox.longestCaption, true) + '"\n' + + ' font.bold: true\n' + + ' visible: false\n' + + '}', + compactListView, "calculateMaxRowWidth"); + width = Math.max(textElement.width, width); + textElement.destroy(); + return width + 16 + 2 * compactTabBox.textMargin + hoverItem.margins.right + hoverItem.margins.left + background.margins.left + background.margins.right; + } + /** + * Calculates the height of one row based on the text height and icon size. + * @return Row height + **/ + function calcRowHeight() { + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "Some Text"\n' + + ' font.bold: true\n' + + ' visible: false\n' + + '}', + compactListView, "calcRowHeight"); + var height = textElement.height; + textElement.destroy(); + // icon size or two text elements and margins and hoverItem margins + return Math.max(16, height + hoverItem.margins.top + hoverItem.margins.bottom); + } + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return compactListView.indexAt(pos.x, pos.y); + } + id: compactListView + objectName: "listView" + // the maximum text width + icon item width (32 + 4 margin) + margins for hover item + margins for background + property int maxRowWidth: calculateMaxRowWidth() + property int rowHeight: calcRowHeight() + // used for image provider URL to trick Qt into reloading icons when the model changes + property int imageId: 0 + anchors { + fill: parent + topMargin: background.margins.top + leftMargin: background.margins.left + rightMargin: background.margins.right + bottomMargin: background.margins.bottom + } + clip: true + delegate: listDelegate + highlight: PlasmaCore.FrameSvgItem { + id: highlightItem + imagePath: "widgets/viewitem" + prefix: "hover" + width: compactListView.width + } + MouseArea { + anchors.fill: parent + onClicked: compactListView.currentIndex = compactListView.indexAt(mouse.x, mouse.y) + } + } +} diff --git a/tabbox/qml/informative.qml b/tabbox/qml/informative.qml new file mode 100644 index 0000000000..1078cca2b0 --- /dev/null +++ b/tabbox/qml/informative.qml @@ -0,0 +1,203 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: informativeTabBox + property int screenWidth : 0 + property int screenHeight : 0 + property bool allDesktops: true + property string longestCaption: "" + property int imagePathPrefix: (new Date()).getTime() + width: Math.min(Math.max(screenWidth * 0.2, listView.maxRowWidth), screenWidth * 0.8) + height: Math.min(Math.max(screenHeight * 0.2, listView.rowHeight * listView.count + background.margins.top + background.margins.bottom), screenHeight * 0.8) + + property int textMargin: 2 + + onLongestCaptionChanged: { + listView.maxRowWidth = listView.calculateMaxRowWidth(); + } + + function setModel(model) { + listView.model = model; + listView.maxRowWidth = listView.calculateMaxRowWidth(); + listView.imageId++; + } + + function modelChanged() { + listView.imageId++; + } + + /** + * Returns the caption with adjustments for minimized items. + * @param caption the original caption + * @param mimized whether the item is minimized + * @return Caption adjusted for minimized state + **/ + function itemCaption(caption, minimized) { + var text = caption; + if (minimized) { + text = "(" + text + ")"; + } + return text; + } + + PlasmaCore.Theme { + id: theme + } + + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + PlasmaCore.FrameSvgItem { + id: background + anchors.fill: parent + imagePath: "dialogs/background" + } + + // delegate + Component { + id: listDelegate + Item { + id: delegateItem + width: listView.width + height: listView.rowHeight + Image { + id: iconItem + source: "image://client/" + index + "/" + informativeTabBox.imagePathPrefix + "-" + listView.imageId + (index == listView.currentIndex ? "/selected" : "/disabled") + width: 32 + height: 32 + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: hoverItem.margins.left + } + } + Text { + id: captionItem + horizontalAlignment: Text.AlignHCenter + text: itemCaption(caption, minimized) + font.bold: true + font.italic: minimized + color: theme.textColor + elide: Text.ElideMiddle + anchors { + left: iconItem.right + right: parent.right + top: parent.top + topMargin: informativeTabBox.textMargin + hoverItem.margins.top + rightMargin: hoverItem.margins.right + } + } + Text { + id: desktopNameItem + horizontalAlignment: Text.AlignHCenter + text: desktopName + font.bold: false + font.italic: true + color: theme.textColor + elide: Text.ElideMiddle + visible: informativeTabBox.allDesktops + anchors { + left: iconItem.right + right: parent.right + top: captionItem.bottom + topMargin: informativeTabBox.textMargin + bottom: parent.bottom + bottomMargin: informativeTabBox.textMargin + hoverItem.margins.bottom + rightMargin: hoverItem.margins.right + } + } + } + } + ListView { + function calculateMaxRowWidth() { + var width = 0; + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "' + itemCaption(informativeTabBox.longestCaption, true) + '"\n' + + ' font.bold: true\n' + + ' visible: false\n' + + '}', + listView, "calculateMaxRowWidth"); + width = Math.max(textElement.width, width); + textElement.destroy(); + return width + 32 + hoverItem.margins.right + hoverItem.margins.left + background.margins.left + background.margins.right; + } + /** + * Calculates the height of one row based on the text height and icon size. + * @return Row height + **/ + function calcRowHeight() { + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "Some Text"\n' + + ' font.bold: true\n' + + ' visible: false\n' + + '}', + listView, "calcRowHeight"); + var height = textElement.height; + textElement.destroy(); + // icon size or two text elements and margins and hoverItem margins + return Math.max(32, height*2 + informativeTabBox.textMargin * 3 + hoverItem.margins.top + hoverItem.margins.bottom); + } + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return listView.indexAt(pos.x, pos.y); + } + id: listView + objectName: "listView" + // the maximum text width + icon item width (32 + 4 margin) + margins for hover item + margins for background + property int maxRowWidth: calculateMaxRowWidth() + property int rowHeight: calcRowHeight() + // used for image provider URL to trick Qt into reloading icons when the model changes + property int imageId: 0 + anchors { + fill: parent + topMargin: background.margins.top + leftMargin: background.margins.left + rightMargin: background.margins.right + bottomMargin: background.margins.bottom + } + clip: true + delegate: listDelegate + highlight: PlasmaCore.FrameSvgItem { + id: highlightItem + imagePath: "widgets/viewitem" + prefix: "hover" + width: listView.width + } + MouseArea { + anchors.fill: parent + onClicked: listView.currentIndex = listView.indexAt(mouse.x, mouse.y) + } + } +} diff --git a/tabbox/qml/small_icons.qml b/tabbox/qml/small_icons.qml new file mode 100644 index 0000000000..8d8c6c40cf --- /dev/null +++ b/tabbox/qml/small_icons.qml @@ -0,0 +1,58 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: smallIconsTabBox + property int screenWidth : 0 + property int screenHeight : 0 + property int imagePathPrefix: (new Date()).getTime() + width: Math.min(Math.max(screenWidth * 0.1, (icons.iconSize + icons.margins.left + icons.margins.right) * icons.count + background.margins.left + background.margins.bottom), screenWidth * 0.9) + height: Math.min(Math.max(screenHeight * 0.05, icons.iconSize + icons.margins.top + icons.margins.bottom + background.margins.top + background.margins.bottom), screenHeight * 0.5) + + + function setModel(model) { + icons.setModel(model); + } + + function modelChanged() { + icons.modelChanged(); + } + + PlasmaCore.FrameSvgItem { + id: background + anchors.fill: parent + imagePath: "dialogs/background" + } + + IconTabBox { + id: icons + iconSize: 16 + anchors { + fill: parent + topMargin: background.margins.top + rightMargin: background.margins.right + bottomMargin: background.margins.bottom + leftMargin: background.margins.left + } + } +} diff --git a/tabbox/qml/tabbox.qml b/tabbox/qml/tabbox.qml new file mode 100644 index 0000000000..dd8cc09338 --- /dev/null +++ b/tabbox/qml/tabbox.qml @@ -0,0 +1,73 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 + +Loader { + id: loader + property int screenWidth : 0 + property int screenHeight : 0 + property bool allDesktops: true + property string longestCaption: "" + onLoaded: { + if (item.screenWidth != undefined) { + item.screenWidth = screenWidth; + } + if (item.screenHeight != undefined) { + item.screenHeight = screenHeight; + } + if (item.allDesktops != undefined) { + item.allDesktops = allDesktops; + } + if (item.longestCaption != undefined) { + item.longestCaption = longestCaption; + } + if (item.setModel) { + item.setModel(clientModel); + } + } + onScreenWidthChanged: { + if (item && item.screenWidth != undefined) { + item.screenWidth = screenWidth; + } + } + onScreenHeightChanged: { + if (item && item.screenHeight != undefined) { + item.screenHeight = screenHeight; + } + } + onAllDesktopsChanged: { + if (item && item.allDesktops != undefined) { + item.allDesktops= allDesktops; + } + } + onLongestCaptionChanged: { + if (item && item.longestCaption != undefined) { + item.longestCaption = longestCaption; + } + } + Connections { + target: clientModel + onModelReset: { + if (item && item.modelChanged) { + item.modelChanged(); + } + } + } +} diff --git a/tabbox/qml/text.qml b/tabbox/qml/text.qml new file mode 100644 index 0000000000..900173c390 --- /dev/null +++ b/tabbox/qml/text.qml @@ -0,0 +1,156 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 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 . +*********************************************************************/ +import QtQuick 1.0 +import org.kde.plasma.core 0.1 as PlasmaCore +import org.kde.qtextracomponents 0.1 + +Item { + id: textTabBox + property int screenWidth : 0 + property int screenHeight : 0 + property string longestCaption: "" + width: Math.min(Math.max(screenWidth * 0.2, textListView.maxRowWidth), screenWidth * 0.8) + height: Math.min(Math.max(screenHeight * 0.2, textListView.rowHeight * textListView.count + background.margins.top + background.margins.bottom), screenHeight * 0.8) + + property int textMargin: 2 + + onLongestCaptionChanged: { + textListView.maxRowWidth = textListView.calculateMaxRowWidth(); + } + + function setModel(model) { + textListView.model = model; + textListView.maxRowWidth = textListView.calculateMaxRowWidth(); + textListView.imageId++; + } + + function modelChanged() { + textListView.imageId++; + } + + PlasmaCore.Theme { + id: theme + } + + // just to get the margin sizes + PlasmaCore.FrameSvgItem { + id: hoverItem + imagePath: "widgets/viewitem" + prefix: "hover" + visible: false + } + + PlasmaCore.FrameSvgItem { + id: background + anchors.fill: parent + imagePath: "dialogs/background" + } + + // delegate + Component { + id: listDelegate + Item { + id: delegateItem + width: textListView.width + height: textListView.rowHeight + Text { + id: captionItem + horizontalAlignment: Text.AlignHCenter + text: caption + color: theme.textColor + elide: Text.ElideMiddle + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + topMargin: hoverItem.margins.top + rightMargin: hoverItem.margins.right + bottomMargin: hoverItem.margins.bottom + leftMargin: hoverItem.margins.left + } + } + } + } + ListView { + function calculateMaxRowWidth() { + var width = 0; + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "' + textTabBox.longestCaption + '"\n' + + ' visible: false\n' + + '}', + textListView, "calculateMaxRowWidth"); + width = Math.max(textElement.width, width); + textElement.destroy(); + return width + hoverItem.margins.right + hoverItem.margins.left + background.margins.left + background.margins.right; + } + /** + * Calculates the height of one row based on the text height and icon size. + * @return Row height + **/ + function calcRowHeight() { + var textElement = Qt.createQmlObject( + 'import Qt 4.7;' + + 'Text {\n' + + ' text: "Some Text"\n' + + ' visible: false\n' + + '}', + textListView, "calcRowHeight"); + var height = textElement.height; + textElement.destroy(); + // icon size or two text elements and margins and hoverItem margins + return height + hoverItem.margins.top + hoverItem.margins.bottom; + } + /** + * Called from C++ to get the index at a mouse pos. + **/ + function indexAtMousePos(pos) { + return textListView.indexAt(pos.x, pos.y); + } + id: textListView + objectName: "listView" + // the maximum text width + icon item width (32 + 4 margin) + margins for hover item + margins for background + property int maxRowWidth: calculateMaxRowWidth() + property int rowHeight: calcRowHeight() + // used for image provider URL to trick Qt into reloading icons when the model changes + property int imageId: 0 + anchors { + fill: parent + topMargin: background.margins.top + leftMargin: background.margins.left + rightMargin: background.margins.right + bottomMargin: background.margins.bottom + } + clip: true + delegate: listDelegate + highlight: PlasmaCore.FrameSvgItem { + id: highlightItem + imagePath: "widgets/viewitem" + prefix: "hover" + width: textListView.width + } + MouseArea { + anchors.fill: parent + onClicked: textListView.currentIndex = textListView.indexAt(mouse.x, mouse.y) + } + } +} diff --git a/tabbox/tabboxhandler.cpp b/tabbox/tabboxhandler.cpp index ef9f73afa4..3e22dffe5e 100644 --- a/tabbox/tabboxhandler.cpp +++ b/tabbox/tabboxhandler.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . // tabbox #include "clientitemdelegate.h" #include "clientmodel.h" +#include "declarative.h" #include "desktopitemdelegate.h" #include "desktopmodel.h" #include "itemlayoutconfig.h" @@ -77,6 +78,7 @@ public: // members TabBoxConfig config; TabBoxView* view; + DeclarativeView *m_declarativeView; ClientModel* m_clientModel; DesktopModel* m_desktopModel; QModelIndex index; @@ -91,6 +93,7 @@ public: TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) : view(NULL) + , m_declarativeView(NULL) { this->q = q; isShown = false; @@ -110,6 +113,7 @@ TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q) TabBoxHandlerPrivate::~TabBoxHandlerPrivate() { delete view; + delete m_declarativeView; } void TabBoxHandlerPrivate::createView() @@ -183,8 +187,14 @@ void TabBoxHandlerPrivate::updateHighlightWindows() WId wId; QVector< WId > data; - if (config.isShowTabBox() && view) { - wId = view->winId(); + QWidget *w = NULL; + if (view && view->isVisible()) { + w = view; + } else if (m_declarativeView && m_declarativeView->isVisible()) { + w = m_declarativeView; + } + if (config.isShowTabBox() && w) { + wId = w->winId(); data.resize(2); data[ 1 ] = wId; } else { @@ -401,11 +411,20 @@ void TabBoxHandler::show() d->updateOutline(); } if (d->config.isShowTabBox()) { - if (!d->view) { - d->createView(); + if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { + // use declarative view + if (!d->m_declarativeView) { + d->m_declarativeView = new DeclarativeView(d->clientModel()); + } + d->m_declarativeView->show(); + d->m_declarativeView->setCurrentIndex(d->index); + } else { + if (!d->view) { + d->createView(); + } + d->view->show(); + d->view->updateGeometry(); } - d->view->show(); - d->view->updateGeometry(); } if (d->config.isHighlightWindows()) { d->updateHighlightWindows(); @@ -424,6 +443,9 @@ void TabBoxHandler::hide(bool abort) if (d->view) { d->view->hide(); } + if (d->m_declarativeView) { + d->m_declarativeView->hide(); + } } QModelIndex TabBoxHandler::nextPrev(bool forward) const @@ -512,6 +534,9 @@ void TabBoxHandler::setCurrentIndex(const QModelIndex& index) if (d->view) { d->view->setCurrentIndex(index); } + if (d->m_declarativeView) { + d->m_declarativeView->setCurrentIndex(index); + } d->index = index; if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) { if (d->config.isShowOutline()) { @@ -574,20 +599,27 @@ QModelIndex TabBoxHandler::grabbedKeyEvent(QKeyEvent* event) const bool TabBoxHandler::containsPos(const QPoint& pos) const { - if (!d->view) { + QWidget *w = NULL; + if (d->view && d->view->isVisible()) { + w = d->view; + } else if (d->m_declarativeView && d->m_declarativeView->isVisible()) { + w = d->m_declarativeView; + } else { return false; } - return d->view->geometry().contains(pos); + return w->geometry().contains(pos); } QModelIndex TabBoxHandler::indexAt(const QPoint& pos) const { - if (!d->view) { - return QModelIndex(); + if (d->view && d->view->isVisible()) { + QPoint widgetPos = d->view->mapFromGlobal(pos); + return d->view->indexAt(widgetPos); + } else if (d->m_declarativeView && d->m_declarativeView->isVisible()) { + QPoint widgetPos = d->m_declarativeView->mapFromGlobal(pos); + return d->m_declarativeView->indexAt(widgetPos); } - QPoint widgetPos = d->view->mapFromGlobal(pos); - QModelIndex ret = d->view->indexAt(widgetPos); - return ret; + return QModelIndex(); } QModelIndex TabBoxHandler::index(KWin::TabBox::TabBoxClient* client) const