[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-panel-devel
Subject:    Re: [Panel-devel] Plasma and the window manager (Re:
From:       Jason Stubbs <jasonbstubbs () gmail ! com>
Date:       2007-11-24 8:24:48
Message-ID: 200711241724.48271.jasonbstubbs () gmail ! com
[Download RAW message or body]

On Saturday 24 November 2007 16:56:55 Jason Stubbs wrote:
> This patch builds upon the last one by:
> - incorporating and extending Lubos Lunak's hack
> - (partially) supporting horizontal and vertical layouts
> - (partial) embed error checking
>
> The patch itself is a little unreadable as too much has changed, but
> applying and then reviewing the actual source code should be fairly
> straight forward.

Apologies for the influx. I just found that I was doing in updateSize() is 
better done by contentSizeHint() which gets rid of the flashes of white too.
Unless there's any other issues found, this is the last patch. Promise!

-- 
Jason Stubbs

["systray.patch" (text/x-diff)]

Index: systemtray.cpp
===================================================================
--- systemtray.cpp	(revision 740589)
+++ systemtray.cpp	(working copy)
@@ -21,64 +21,114 @@
 
 // Own
 #include "systemtray.h"
-#include "systemtraywidget.h"
 
-// KDE
-#include <KWindowSystem>
-#include <QGraphicsView>
-
 SystemTray::SystemTray(QObject *parent, const QVariantList &arguments)
-    : Plasma::Applet(parent, arguments),
-      m_systemTrayWidget(new SystemTrayWidget(0,
-                  Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint))
-{
-    connect(m_systemTrayWidget, SIGNAL(sizeChanged()), SLOT(updateLayout()));
-    m_systemTrayWidget->show();
-    KWindowSystem::setState(m_systemTrayWidget->winId(),
-            NET::SkipTaskbar | NET::SkipPager | NET::KeepBelow);
-}
+    : Plasma::Applet(parent, arguments)
+{ }
 
 SystemTray::~SystemTray()
 {
+    // Get rid of our SystemTrayWidget if we still have one
     delete m_systemTrayWidget;
 }
 
 QSizeF SystemTray::contentSizeHint() const
 {
-    return QSizeF(m_systemTrayWidget->size());
+    if (!m_currentView) {
+        return QSizeF();
+    }
+    
+    QRect widgetRect;
+    widgetRect.setSize(m_systemTrayWidget->minimumSizeHint());
+
+    // Transform the size into the coordinates used by our QGraphicsView
+    return m_currentView->mapToScene(widgetRect).boundingRect().size();
 }
 
 Qt::Orientations SystemTray::expandingDirections() const
 {
-    // simplify layouting by giving the system tray a fixed
-    // size for now
+    // Extra space isn't useful in either direction
     return 0;
 }
 
 QVariant SystemTray::itemChange(QGraphicsItem::GraphicsItemChange change, const \
QVariant &value)  {
-    if (change == ItemPositionHasChanged) {
-        //figure out where this applet really is.
-        QPoint realPos(0, 0); //start with a default that's at least visible
-        QGraphicsScene *s=scene();
-        if (s) {
-            QList<QGraphicsView *> viewlist = s->views();
-            foreach (QGraphicsView *v, viewlist) {
-                QRectF r=v->sceneRect();
-                //now find out if this applet is actually on here
-                //I consider it "on here" if our pos is within the rect
-                //but there may be other valid ways to do this
-                if (r.contains(scenePos())) {
-                    kDebug() << "using view" << v;
-                    QPoint p=v->mapFromScene(scenePos());
-                    realPos = v->mapToGlobal(p);
-                    break; //no, I don't care if other views show it
-                }
-            }
+    // We've been added to a scene
+    if (change == ItemSceneChange) {
+        // If we were previously part of a different scene,
+        // stop monitoring it for changes
+        if (m_currentScene) {
+            disconnect(m_currentScene, SIGNAL(changed(const QList<QRectF> &)),
+                       this, SLOT(handleSceneChange(const QList<QRectF> &)));
         }
-        m_systemTrayWidget->move(realPos);
+        // Make a note of what scene we're on and start
+        // monitoring it for changes
+        // We need to monitor all changes because a QGraphicsItem normally
+        // only get notified of changes relative to its parent but we need
+        // to know about changes relative to the view to be able to correctly
+        // place our SystemTrayWidget
+        m_currentScene = value.value<QGraphicsScene *>();
+        if (m_currentScene) {
+            connect(m_currentScene, SIGNAL(changed(const QList<QRectF> &)),
+                    this, SLOT(handleSceneChange(const QList<QRectF> &)));
+        }
     }
     return Plasma::Applet::itemChange(change, value);
 }
 
+void SystemTray::handleSceneChange(const QList<QRectF> & region)
+{
+    // Don't do anything if none of the scene changes affect us
+    bool affected = false;
+    foreach (QRectF rect, region) {
+        if (rect.contains(scenePos())) {
+            affected = true;
+            break;
+        }
+    }
+    if (!affected) {
+        return;
+    }
+
+    // Find out which QGraphicsView (if any) that we are
+    // visible on
+    // We may be visible on more than one QGraphicsView but
+    // we only take the first because we are only able to
+    // display one system tray
+    QGraphicsView *view = 0;
+    foreach (view, m_currentScene->views()) {
+        if (view->sceneRect().contains(scenePos())) {
+            break;
+        }
+        view = 0;
+    }
+    if (!view) {
+        return;
+    }
+
+    // If the view is different to the view we were previously visible on
+    // or we had no view up until now, recreate our SystemTrayWidget
+    if (view != m_currentView) {
+        m_currentView = view;
+        // Reparent or create our SystemTrayWidget under the current view
+        if (m_systemTrayWidget) {
+            m_systemTrayWidget->setParent(m_currentView);
+        } else {
+            m_systemTrayWidget = new SystemTrayWidget(view);
+            connect(m_systemTrayWidget, SIGNAL(sizeShouldChange()), this, \
SLOT(updateSize())); +        }
+        m_systemTrayWidget->setVisible(true);
+    }
+
+    // Set our SystemTrayWidget's size and position equal to that of this item
+    QRect rect = m_currentView->mapFromScene(sceneBoundingRect()).boundingRect();
+    m_systemTrayWidget->setMaximumSize(rect.size());
+    m_systemTrayWidget->setGeometry(rect);
+}
+
+void SystemTray::updateSize()
+{
+    updateGeometry();
+}
+
 #include "systemtray.moc"
Index: systemtraywidget.cpp
===================================================================
--- systemtraywidget.cpp	(revision 740589)
+++ systemtraywidget.cpp	(working copy)
@@ -28,8 +28,6 @@
 
 // Qt
 #include <QEvent>
-#include <QHBoxLayout>
-#include <QX11EmbedContainer>
 #include <QX11Info>
 
 // Xlib
@@ -45,30 +43,69 @@
 };
 }
 
-SystemTrayWidget::SystemTrayWidget(QWidget *parent, Qt::WindowFlags f)
-    : QWidget(parent, f)
+SystemTrayContainer::SystemTrayContainer(WId clientId, QWidget *parent)
+    : QX11EmbedContainer(parent)
 {
-    m_layout = new QHBoxLayout(this);
-    setLayout(m_layout);
-    QPalette newPalette = palette();
-    newPalette.setBrush(QPalette::Window, Qt::black);
-    setPalette(newPalette);
-    KWindowSystem::setState(winId(), NET::Sticky | NET::KeepAbove);
-    init();
+    connect(this, SIGNAL(clientClosed()), SLOT(deleteLater()));
+    connect(this, SIGNAL(error(QX11EmbedContainer::Error)), \
SLOT(handleError(QX11EmbedContainer::Error))); +
+    // Tray icons have a fixed size of 22x22
+    setMinimumSize(22, 22);
+
+    // HACK: Tell the client to draw it's own black background
+    // rather than taking ours when it's depth is different as
+    // mixing of different color depths will fail
+    XWindowAttributes clientAttr;
+    XGetWindowAttributes(QX11Info::display(), clientId, &clientAttr);
+    if (clientAttr.depth != QX11Info::appDepth()) {
+        XSetWindowBackgroundPixmap(QX11Info::display(), clientId, None);
+        XSetWindowBackground(QX11Info::display(), clientId, 0 /* black */);
+    }
+
+    kDebug() << "attempting to embed" << clientId;
+    embedClient(clientId);
+
+#if 0
+    // BUG: error() sometimes return Unknown even on success
+    if (error() == Unknown || error() == InvalidWindowID) {
+        kDebug() << "embedding failed for" << clientId;
+        deleteLater();
+    }
+#endif
 }
 
-bool SystemTrayWidget::x11Event(XEvent *event)
+void SystemTrayContainer::handleError(QX11EmbedContainer::Error error)
 {
-    if (event->type == ClientMessage) {
-        if (event->xclient.message_type == m_opcodeAtom &&
-            event->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
-            embedWindow((WId)event->xclient.data.l[2]);
-            return true;
-        }
-    }
-    return QWidget::x11Event(event);
+    Q_UNUSED(error);
+    deleteLater();
 }
 
+SystemTrayWidget::SystemTrayWidget(QWidget *parent, Qt::WindowFlags f)
+    : QWidget(parent, f),
+    // HACK: We need a better way to find out our orientation and when it changes
+    m_orientation(parent->width() > parent->height() ? Qt::Horizontal : \
Qt::Vertical), +    m_nextRow(0),
+    m_nextColumn(0)
+{
+    // Add stretches around our QGridLayout so tray icons are centered
+    // and don't get any more space than they need
+    QHBoxLayout* hLayout = new QHBoxLayout();
+    QVBoxLayout* vLayout = new QVBoxLayout();
+    m_mainLayout = new QGridLayout();
+
+    hLayout->addStretch();
+    hLayout->addLayout(vLayout);
+    hLayout->addStretch();
+
+    vLayout->addStretch();
+    vLayout->addLayout(m_mainLayout);
+    vLayout->addStretch();
+
+    setLayout(hLayout);
+
+    init();
+}
+
 void SystemTrayWidget::init()
 {
     Display *display = QX11Info::display();
@@ -95,51 +132,80 @@
     }
 }
 
-bool SystemTrayWidget::event(QEvent *event)
+bool SystemTrayWidget::x11Event(XEvent *event)
 {
-    if (event->type() == QEvent::LayoutRequest) {
-        resize(minimumSize());
-        emit sizeChanged();
+    if (event->type == ClientMessage) {
+        if (event->xclient.message_type == m_opcodeAtom &&
+            event->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+
+            // Set up a SystemTrayContainer for the client
+            SystemTrayContainer *container = new \
SystemTrayContainer((WId)event->xclient.data.l[2], this); +            \
addWidgetToLayout(container); +
+            connect(container, SIGNAL(clientIsEmbedded()), this, \
SIGNAL(sizeShouldChange())); +            connect(container, SIGNAL(destroyed(QObject \
*)), this, SLOT(removeContainer(QObject *))); +
+            return true;
+        }
     }
-    return QWidget::event(event);
+    return QWidget::x11Event(event);
 }
 
-void SystemTrayWidget::embedWindow(WId id)
+void SystemTrayWidget::addWidgetToLayout(QWidget *widget)
 {
-    kDebug() << "trying to add window with id " << id;
-    if (! m_containers.contains(id)) {
-        QX11EmbedContainer *container = new QX11EmbedContainer(this);
-        container->embedClient(id);
-        // TODO: add error handling
-        m_layout->addWidget(container);
-        container->show();
-        m_containers[id] = container;
-        connect(container, SIGNAL(clientClosed()), this, SLOT(windowClosed()) );
-        kDebug() << "SystemTray: Window with id " << id << "added" << container;
+    // Figure out where it should go and add it to our layout
+    if (m_orientation == Qt::Horizontal) {
+        // Add down then across when horizontal
+        if (m_mainLayout->minimumSize().height() + widget->minimumHeight() > \
maximumHeight()) { +            m_nextColumn++;
+            m_nextRow = 0;
+        }
+        m_mainLayout->addWidget(widget, m_nextRow, m_nextColumn);
+        m_nextRow++;
+    } else {
+        // Add across then down when vertical
+        if (m_mainLayout->minimumSize().width() + widget->minimumWidth() > \
maximumWidth()) { +            m_nextRow++;
+            m_nextColumn = 0;
+        }
+        m_mainLayout->addWidget(widget, m_nextRow, m_nextColumn);
+        m_nextColumn++;
     }
 }
 
-//what exactly is this for? is it related to QX11EmbedContainer::discardClient? why \
                is it blank?
-void SystemTrayWidget::discardWindow(WId)
+void SystemTrayWidget::removeContainer(QObject *container)
 {
-}
-
-void SystemTrayWidget::windowClosed()
-{
-    kDebug() << "Window closed";
-    //by this point the window id is gone, so we have to iterate to find out who's \
                lost theirs
-    ContainersList::iterator i = m_containers.begin();
-    while (i != m_containers.end()) {
-        QX11EmbedContainer *c=i.value();
-        if (c->clientWinId()==0) {
-            i=m_containers.erase(i);
-            kDebug() << "deleting container" << c;
-            delete c;
-            //do NOT assume that there will never be more than one without an id
-            continue;
+    // Pull all widgets from our container, skipping over the
+    // one that was just deleted
+    QList<QWidget *> remainingWidgets;
+    while (QLayoutItem* item = m_mainLayout->takeAt(0)) {
+        if (item->widget() && item->widget() != container) {
+            remainingWidgets.append(item->widget());
         }
-        ++i;
+        delete item;
     }
+
+    // Reset the widths and heights in our layout to 0 so that
+    // the removed widget's space isn't kept
+    // (Why doesn't QGridLayout do this automatically?)
+    for (int row = 0; row < m_mainLayout->rowCount(); row++) {
+        m_mainLayout->setRowMinimumHeight(row, 0);
+    }
+    for (int column = 0; column < m_mainLayout->columnCount(); column++) {
+        m_mainLayout->setColumnMinimumWidth(column, 0);
+    }
+
+    // Re-add remaining widgets
+    m_nextRow = 0;
+    m_nextColumn = 0;
+    foreach (QWidget *widget, remainingWidgets) {
+        addWidgetToLayout(widget);
+    }
+
+    // Force a layout so that minimumSizeHint() returns the
+    // correct value and signal that our size should change
+    layout()->activate();
+    emit sizeShouldChange();
 }
 
 #include "systemtraywidget.moc"
Index: systemtray.h
===================================================================
--- systemtray.h	(revision 740589)
+++ systemtray.h	(working copy)
@@ -22,11 +22,17 @@
 #ifndef SYSTEMTRAY_H
 #define SYSTEMTRAY_H
 
+// Own
+#include "systemtraywidget.h"
+
+// Qt
+#include <QGraphicsScene>
+#include <QGraphicsView>
+#include <QPointer>
+
 // Plasma
 #include <plasma/applet.h>
 
-class SystemTrayWidget;
-
 class SystemTray: public Plasma::Applet
 {
 Q_OBJECT
@@ -36,18 +42,20 @@
     ~SystemTray();
 
     QSizeF contentSizeHint() const;
-
     Qt::Orientations expandingDirections() const;
 
 protected:
     QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant \
&value);  
 private slots:
-    void updateLayout()
-    { update(); }
+    void updateSize();
+    void handleSceneChange(const QList<QRectF> & region);
 
 private:
-    SystemTrayWidget *m_systemTrayWidget;
+    // These can all be deleted externally so we guard them
+    QPointer<SystemTrayWidget> m_systemTrayWidget;
+    QPointer<QGraphicsScene> m_currentScene;
+    QPointer<QGraphicsView> m_currentView;
 };
 
 K_EXPORT_PLASMA_APPLET(systemtray, SystemTray)
Index: systemtraywidget.h
===================================================================
--- systemtraywidget.h	(revision 740589)
+++ systemtraywidget.h	(working copy)
@@ -23,15 +23,24 @@
 #define QSYSTRAY_H
 
 // Qt
+#include <QGridLayout>
 #include <QWidget>
-#include <QHash>
+#include <QX11EmbedContainer>
 
 // Xlib
 #include <X11/Xdefs.h>
 
-class QHBoxLayout;
-class QX11EmbedContainer;
+class SystemTrayContainer: public QX11EmbedContainer
+{
+Q_OBJECT
 
+public:
+    SystemTrayContainer(WId client, QWidget *parent);
+
+private slots:
+    void handleError(QX11EmbedContainer::Error error);
+};
+
 class SystemTrayWidget: public QWidget
 {
 Q_OBJECT
@@ -41,25 +50,23 @@
 
 protected:
     bool x11Event(XEvent *event);
-    bool event(QEvent *event);
 
 Q_SIGNALS:
-    void sizeChanged();
+    void sizeShouldChange();
 
 private slots:
+    void removeContainer(QObject *container);
+
+private:
+    void addWidgetToLayout(QWidget *widget);
     void init();
-    void embedWindow(WId id);
-    void discardWindow(WId id);
-    /**
-     * Removes the container with id 0 from our list.
-     */
-    void windowClosed();
 
-private:
-    typedef QHash<WId, QX11EmbedContainer*> ContainersList;
-    
-    ContainersList m_containers;
-    QHBoxLayout *m_layout;
+    QGridLayout* m_mainLayout;
+    Qt::Orientation m_orientation;
+    int m_nextRow;
+    int m_nextColumn;
+
+    // These need to remain allocated for the duration of our lifetime
     Atom m_selectionAtom;
     Atom m_opcodeAtom;
 };



_______________________________________________
Panel-devel mailing list
Panel-devel@kde.org
https://mail.kde.org/mailman/listinfo/panel-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic