[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-23 11:58:31
Message-ID: 200711232058.31596.jasonbstubbs () gmail ! com
[Download RAW message or body]

On Friday 23 November 2007 19:13:38 Chani wrote:
> On November 23, 2007 17:23:10 Jason Stubbs wrote:
> > The patch is attached. I ended up cleaning up and/or simplifying more
> > than is necessary for reparenting, but it should still be fairly easy to
> > follow. There's current three issues that I know of:
>
> you removed a comment from the code for no apparent reason, and your own
> code has ZERO comments.
> please, please, comment your code. uncommented code makes baby jesus cry.

I'm more of the school that unreadable code make baby jesus cry. But I guess 
my code wasn't readable enough, so I've gone through and commented everything 
though. :)

> as for the code itself, I'm not sure, because I'm too tired to follow
> uncommented code - but several things look kinda suspicious, and some stuff
> is removed that maybe shouldn't be... for one thing, don't assume that
> you're going to get a clientClosed() signal. iirc, sometimes systray icons
> go away without bothering to tell the systray (scim was one, I think).

That was the only thing I wasn't sure would be safe to remove given the 
comment that was previously there. However, testing `kill -9 $(pidof kmix)` 
worked fine so I think it should be safe. Even with the current code, the 
icon is going to hang around at least until something else causes a 
clientClosed() so it's an issue either way.

-- 
Jason Stubbs

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

Index: systemtray.cpp
===================================================================
--- systemtray.cpp	(revision 740358)
+++ systemtray.cpp	(working copy)
@@ -25,60 +25,122 @@
 
 // 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());
-}
-
 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;
+        // If the previous view still exists and is just not displaying us
+        // anymore, we need to delete the SystemTrayWidget that is parented
+        // to it
+        delete m_systemTrayWidget;
+        // Create a new SystemTrayWidget as a child of the current view and
+        // ask to be notified when it should change size
+        m_systemTrayWidget = new SystemTrayWidget(view);
+        m_systemTrayWidget->setVisible(true);
+        connect(m_systemTrayWidget, SIGNAL(sizeShouldChange()), this, SLOT(updateSize()));
+    }
+
+    // Set our SystemTrayWidget's size and position equal to that of this item
+    QRect rect = m_currentView->mapFromScene(sceneBoundingRect()).boundingRect();
+    m_systemTrayWidget->setGeometry(rect);
+}
+
+void SystemTray::updateSize()
+{
+    // We should always have a view if we're getting signals from a
+    // SystemTrayWidget but just in case...
+    if (!m_currentView) {
+        return;
+    }
+
+    // Get the minimum size allowed by our SystemTrayWidget
+    QRect widgetRect;
+    widgetRect.setSize(m_systemTrayWidget->minimumSizeHint());
+    
+    // Transform the size into the coordinates used by our QGraphicsView
+    QSizeF newSize = m_currentView->mapToScene(widgetRect).boundingRect().size();
+
+    // If our minimum size is the same as same as our SystemTrayWidget's
+    // minimum size then we don't need to do anything
+    if (minimumSize() != newSize) {
+        // Otherwise, set our minimum size and inform the layout that
+        // things need to be adjusted accordingly
+        setMinimumSize(newSize);
+        resize(newSize);
+        updateGeometry();
+    }
+}
+
 #include "systemtray.moc"
Index: systemtraywidget.cpp
===================================================================
--- systemtraywidget.cpp	(revision 740358)
+++ systemtraywidget.cpp	(working copy)
@@ -48,12 +48,16 @@
 SystemTrayWidget::SystemTrayWidget(QWidget *parent, Qt::WindowFlags f)
     : QWidget(parent, f)
 {
-    m_layout = new QHBoxLayout(this);
-    setLayout(m_layout);
+    // TODO: Support vertical layout and multiple lines of icons
+    setLayout(new QHBoxLayout(this));
+    
+    // FIXME: We should really use Qt::transparent, but it does not
+    // seem to be working with QX11EmbedContainer at the moment
     QPalette newPalette = palette();
-    newPalette.setBrush(QPalette::Window, Qt::black);
+    newPalette.setColor(QPalette::Window, Qt::black);
     setPalette(newPalette);
-    KWindowSystem::setState(winId(), NET::Sticky | NET::KeepAbove);
+    setBackgroundRole(QPalette::Window);
+    
     init();
 }
 
@@ -62,7 +66,21 @@
     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]);
+            // Set up a new QX11EmbedContainer and embed the requested
+            // Window ID into it
+            QX11EmbedContainer *container = new QX11EmbedContainer(this);
+            container->embedClient((WId)event->xclient.data.l[2]);
+            // This minimizes flicker for the time being
+            container->setAutoFillBackground(false);
+            layout()->addWidget(container);
+            
+            // Let the outside know that our size should change once the
+            // client has finished embedding or when the client closes
+            connect(container, SIGNAL(clientIsEmbedded()), this, SIGNAL(sizeShouldChange()));
+            connect(container, SIGNAL(clientClosed()), this, SIGNAL(sizeShouldChange()));
+            // When the client closes, also get rid of the container
+            connect(container, SIGNAL(clientClosed()), container, SLOT(deleteLater()));
+            
             return true;
         }
     }
@@ -95,51 +113,4 @@
     }
 }
 
-bool SystemTrayWidget::event(QEvent *event)
-{
-    if (event->type() == QEvent::LayoutRequest) {
-        resize(minimumSize());
-        emit sizeChanged();
-    }
-    return QWidget::event(event);
-}
-
-void SystemTrayWidget::embedWindow(WId id)
-{
-    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;
-    }
-}
-
-//what exactly is this for? is it related to QX11EmbedContainer::discardClient? why is it blank?
-void SystemTrayWidget::discardWindow(WId)
-{
-}
-
-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;
-        }
-        ++i;
-    }
-}
-
 #include "systemtraywidget.moc"
Index: systemtray.h
===================================================================
--- systemtray.h	(revision 740358)
+++ systemtray.h	(working copy)
@@ -22,6 +22,11 @@
 #ifndef SYSTEMTRAY_H
 #define SYSTEMTRAY_H
 
+// Qt
+#include <QGraphicsScene>
+#include <QGraphicsView>
+#include <QPointer>
+
 // Plasma
 #include <plasma/applet.h>
 
@@ -35,19 +40,22 @@
     SystemTray(QObject *parent, const QVariantList &arguments = QVariantList());
     ~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;
+    void initSystemTrayWidget(QWidget* parent);
+
+    // 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 740358)
+++ systemtraywidget.h	(working copy)
@@ -24,13 +24,10 @@
 
 // Qt
 #include <QWidget>
-#include <QHash>
 
 // Xlib
 #include <X11/Xdefs.h>
 
-class QHBoxLayout;
-class QX11EmbedContainer;
 
 class SystemTrayWidget: public QWidget
 {
@@ -41,25 +38,15 @@
 
 protected:
     bool x11Event(XEvent *event);
-    bool event(QEvent *event);
 
 Q_SIGNALS:
-    void sizeChanged();
+    void sizeShouldChange();
 
-private slots:
+private:
     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;
+    // 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