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

List:       kde-panel-devel
Subject:    Re: [Panel-devel] minimization to taskbar
From:       Jason Stubbs <jasonbstubbs () gmail ! com>
Date:       2007-12-02 10:23:44
Message-ID: 200712021923.44580.jasonbstubbs () gmail ! com
[Download RAW message or body]

On Sunday 02 December 2007 06:11:53 Aaron J. Seigo wrote:
> On Saturday 01 December 2007, Jason Stubbs wrote:
> > On Sunday 02 December 2007 00:00:52 Robert Knight wrote:
> > I am aware that paint() isn't a good place to do it though. Even
> > listening for ItemPositionChanged isn't enough as that event only happens
> > when the position has changed with regard to the immediate parent. I
> > guess I will need to add an event listener like I did with the system
> > tray. I wonder if that functionality couldn't be moved into Widget as
> > well...
>
> i like the idea of this going into Widget when it is ready. i don't think
> 4.4 will remove the need for that code as it's mapping between the sceen
> and x11 geometries, something i'm not sure is there even in the 4.4 widget
> support? (i could be wrong on that)

As discussed on IRC, the main issue is that items only know when they have 
changed position relative to their parent's geometry. I can't tell from the 
documentation, but using a QWidget sub-class that overrides moveEvent() in 
conjunction with QGraphicsWidgetProxy might suffice.

Also as per IRC, I've had a look at getting parent widgets to pass 
ItemPositionHasChanged events down to children that want to receive them.
I've attached patches to widget and systemtray separately. Most of the 
codepaths are untested though as it seems that systemtray is a top-level 
widget on the panel, which implies that checking for scene changes is 
unnecessary work in the current code.

Kudos to anybody who can think of better function and variables names. ;)

Also, this still doesn't address a view changing the scene rect that it is 
viewing or any other transformations (like zoom).

> > On Sunday 02 December 2007 00:00:52 Robert Knight wrote:
> > > - Both TaskGroupItem and AbstractTaskItem implement
> > > publishIconGeometry(), which means that when both the task and the
> > > parent (a TaskGroupItem) are repainted, both will publish icon
> > > geometry for the task.
> >
> > I had guessed that this would happen, but wasn't really going to worry
> > about it. Is it a problem? Even after getting position and size change
> > handling above correct, animations will cause the icon geometry publish
> > to occur several times a second. If it's not a problem, I'd like to leave
> > this
>
> these can easily be compressed with a timer, though. also, the layout
> animator is rather broken right now and needs to be fixed so that items
> stop animating a *lot* sooner than they do right now (run with
> QT_FLUSH_PAINT to see the full effect of the animations); the animator is
> doing too many small fine adjustments, particularly at the end, that just
> don't matter visually.

I haven't looked at this aspect yet. I think I might leave the taskbar until 
a "final" solution to the systray handling above is agreed upon.

-- 
Jason Stubbs

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

Index: widgets/widget.cpp
===================================================================
--- widgets/widget.cpp	(revision 743900)
+++ widgets/widget.cpp	(working copy)
@@ -25,6 +25,8 @@
 #include <limits>
 
 #include <QApplication>
+#include <QGraphicsScene>
+#include <QGraphicsView>
 #include <QList>
 #include <QPainter>
 #include <QPixmapCache>
@@ -47,7 +49,8 @@ class Widget::Private
                           std::numeric_limits<qreal>::infinity()),
               opacity(1.0),
               cachePaintMode(Widget::NoCacheMode),
-              wasMovable(false)
+              wasMovable(false),
+              parentChangePropogated(false)
         { }
         ~Private() { }
 
@@ -67,6 +70,8 @@ class Widget::Private
         QRectF cacheInvalidated;
 
         bool wasMovable;
+        bool parentChangePropogated;
+        QList<Widget *> propogateChildren;
 
         bool shouldPaint(QPainter *painter, const QTransform &transform);
 };
@@ -295,8 +300,11 @@ void Widget::resize(qreal w, qreal h)
 
 Widget *Widget::parent() const
 {
-    QGraphicsItem *parent = parentItem();
+    return searchParent(parentItem());
+}
 
+Widget *Widget::searchParent(QGraphicsItem *parent) const
+{
     while (parent) {
         Widget *parentWidget = dynamic_cast<Widget *>(parentItem());
 
@@ -465,6 +473,27 @@ void Widget::paintWidget(QPainter *paint
 
 QVariant Widget::itemChange(GraphicsItemChange change, const QVariant &value)
 {
+    if (change == QGraphicsItem::ItemPositionHasChanged) {
+        foreach (Widget *child, d->propogateChildren) {
+            child->itemChange(QGraphicsItem::ItemPositionHasChanged, child->pos());
+        }
+    }
+
+    if (change == QGraphicsItem::ItemParentChange) {
+        // If we're receiving ItemPositionHasChanged events from our old parent,
+        // stop doing so and start receiving from the new parent
+        if (d->parentChangePropogated || !d->propogateChildren.isEmpty()) {
+            Widget *parentWidget = parent();
+            if (parentWidget) {
+                parentWidget->removePropogateChild(this);
+            }
+            parentWidget = searchParent(value.value<QGraphicsItem*>());
+            if (parentWidget) {
+                parentWidget->addPropogateChild(this);
+            }
+        }
+    }
+
     if (change == QGraphicsItem::ItemChildRemovedChange) {
         if (layout()) {
             layout()->removeItem(dynamic_cast<Plasma::LayoutItem*>(value.value<QGraphicsItem*>()));
@@ -487,5 +516,91 @@ void Widget::managingLayoutChanged()
     }
 }
 
+bool Widget::parentChangePropogated()
+{
+    return d->parentChangePropogated;
+}
+
+void Widget::setParentChangePropogated(bool propogated)
+{
+    if (propogated == d->parentChangePropogated) {
+        return;
+    }
+    d->parentChangePropogated = propogated;
+
+    // If we are passing ItemPositionHasChanged events to any of our children
+    // we need to continue receiving from our parent
+    if (!d->propogateChildren.isEmpty()) {
+        return;
+    }
+
+    // Otherwise update our status with regards to our parent based on the
+    // new setting
+    Widget *parentWidget = parent();
+    if (parentWidget) {
+        if (propogated) {
+            parentWidget->addPropogateChild(this);
+        } else {
+            parentWidget->removePropogateChild(this);
+        }
+    }
+}
+
+void Widget::addPropogateChild(Widget *child)
+{
+    // Start passing ItemPositionHasChanged events to child and also tell our
+    // parent to start passing them to use if it isn't already
+    if (!d->parentChangePropogated && d->propogateChildren.isEmpty()) {
+        Widget *parentWidget = parent();
+        if (parentWidget) {
+            parentWidget->addPropogateChild(this);
+        }
+    }
+    Q_ASSERT(!d->propogateChildren.contains(child));
+    d->propogateChildren.append(child);
+}
+
+void Widget::removePropogateChild(Widget *child)
+{
+    // Stop passing ItemPositionHasChanged events to child and also tell our
+    // parent to stop passing them to use if no longer necessary
+    Q_ASSERT(d->propogateChildren.contains(child));
+    d->propogateChildren.removeAll(child);
+    if (!d->parentChangePropogated && d->propogateChildren.isEmpty()) {
+        Widget *parentWidget = parent();
+        if (parentWidget) {
+            parentWidget->removePropogateChild(this);
+        }
+    }
+}
+
+QList<QGraphicsView *> Widget::associatedViews() const
+{
+    QList<QGraphicsView *> views;
+    if (!scene()) {
+        return views;
+    }
+    foreach (QGraphicsView *view, scene()->views()) {
+        if (view->sceneRect().intersects(sceneBoundingRect())) {
+            views.append(view);
+        }
+    }
+    return views;
+}
+
+QRectF Widget::mapFromView(const QGraphicsView *view, const QRect &rect) const
+{
+    // TODO: Confirm that adjusted() is needed and is not covering for some
+    // issue elsewhere
+    return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1);
+}
+
+QRect Widget::mapToView(const QGraphicsView *view, const QRectF &rect) const
+{
+    // TODO: Confirm that adjusted() is needed and is not covering for some
+    // issue elsewhere
+    return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1);
+}
+
 } // Plasma namespace
 
Index: widgets/widget.h
===================================================================
--- widgets/widget.h	(revision 743900)
+++ widgets/widget.h	(working copy)
@@ -30,6 +30,8 @@
 #include <plasma/layouts/layoutitem.h>
 #include <plasma/plasma_export.h>
 
+class QGraphicsView;
+
 namespace Plasma
 {
 
@@ -242,6 +244,25 @@ TODO: implement once we decide how to ha
 
     virtual QGraphicsItem* graphicsItem();
 
+    /**
+     * Returns a list of QGraphicsViews that are currently displaying the widget.
+     */
+    QList<QGraphicsView *> associatedViews() const;
+
+    /**
+     * Maps a QRect from a view's coordinates to local coordinates.
+     * @param view the view from which rect should be mapped
+     * @param rect the rect to be mapped
+     */
+    QRectF mapFromView(const QGraphicsView *view, const QRect &rect) const;
+
+    /**
+     * Maps a QRectF from local coordinates to a view's coordinates.
+     * @param view the view to which rect should be mapped
+     * @param rect the rect to be mapped
+     */
+    QRect mapToView(const QGraphicsView *view, const QRectF &rect) const;
+
 protected:
     /**
      * Paints the widget
@@ -255,8 +276,18 @@ protected:
     void setSize(const QSizeF& size);
     void managingLayoutChanged();
 
+    /**
+     * Sets whether parent ItemPositionHasChanged events are propogated
+     * @param propogated whether to propogate
+     */
+    void setParentChangePropogated(bool propogated);
+    bool parentChangePropogated();
+
 private:
     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+    void addPropogateChild(Widget *child);
+    void removePropogateChild(Widget *child);
+    Widget *searchParent(QGraphicsItem *parent) const;
 
     class Private;
     Private *const d;

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

Index: systemtray.cpp
===================================================================
--- systemtray.cpp	(revision 743900)
+++ systemtray.cpp	(working copy)
@@ -25,7 +25,9 @@
 
 SystemTray::SystemTray(QObject *parent, const QVariantList &arguments)
     : Plasma::Applet(parent, arguments)
-{ }
+{
+    setParentChangePropogated(true);
+}
 
 SystemTray::~SystemTray()
 {
@@ -39,13 +41,9 @@ QSizeF SystemTray::contentSizeHint() con
         return QSizeF();
     }
 
-    QRect widgetRect;
-    widgetRect.setSize(m_systemTrayWidget->minimumSizeHint());
-
     // Transform the size into the coordinates used by our QGraphicsView
-    // Using mapToScene() causes us to lose QSize(1, 1) so we add it back
-    QSizeF size = m_currentView->mapToScene(widgetRect).boundingRect().size() + QSize(1, 1);
-    return size;
+    QRect rect(m_systemTrayWidget->pos(), m_systemTrayWidget->minimumSizeHint());
+    return mapFromView(m_currentView, rect).size();;
 }
 
 void SystemTray::constraintsUpdated(Plasma::Constraints constraints)
@@ -63,53 +61,32 @@ Qt::Orientations SystemTray::expandingDi
 
 QVariant SystemTray::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
 {
-    // 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> &)));
-        }
-        // 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 gets 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> &)));
-        }
+    if (change == ItemPositionHasChanged) {
+        updateWidgetGeometry();
     }
+
     return Plasma::Applet::itemChange(change, value);
 }
 
-void SystemTray::handleSceneChange(const QList<QRectF> &region)
+void SystemTray::updateWidgetGeometry()
 {
     // Create or reparent our system tray to the current view and update the
     // widget's geometry to match this item.
 
-    // Don't do anything if none of the scene changes affect us
-    if (!intersectsRegion(region)) {
-        return;
-    }
-
     // Find out which QGraphicsView (if any) that we are visible on
-    QGraphicsView *view = findView();
-    if (!view) {
+    QList<QGraphicsView *> views = associatedViews();
+    if (views.isEmpty()) {
         return;
     }
 
     // If the view is different to the view we were previously visible on or we
     // had no view up until now, (re)create or reparent our SystemTrayWidget
-    if (view != m_currentView) {
-        m_currentView = view;
+    if (views.first() != m_currentView) {
+        m_currentView = views.first();
         if (m_systemTrayWidget) {
             m_systemTrayWidget->setParent(m_currentView);
         } else {
-            m_systemTrayWidget = new SystemTrayWidget(view);
+            m_systemTrayWidget = new SystemTrayWidget(m_currentView);
             connect(m_systemTrayWidget, SIGNAL(sizeShouldChange()), this, SLOT(updateSize()));
         }
         updateWidgetOrientation();
@@ -117,34 +94,11 @@ void SystemTray::handleSceneChange(const
     }
 
     // Set our SystemTrayWidget's size and position equal to that of this item
-    // Using mapFromScene() causes us to gain QSize(1, 1) so we take it off
-    QRect rect = m_currentView->mapFromScene(sceneBoundingRect()).boundingRect().adjusted(0, 0, -1, -1);
+    QRect rect = mapToView(m_currentView, boundingRect());
     m_systemTrayWidget->setMaximumSize(rect.size());
     m_systemTrayWidget->setGeometry(rect);
 }
 
-bool SystemTray::intersectsRegion(const QList<QRectF> &region)
-{
-   foreach (const QRectF &rect, region) {
-        if (rect.intersects(sceneBoundingRect())) {
-            return true;
-        }
-    }
-    return false;
-}
-
-QGraphicsView * SystemTray::findView()
-{
-    // 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
-    foreach (QGraphicsView *view, m_currentScene->views()) {
-        if (view->sceneRect().contains(scenePos())) {
-            return view;
-        }
-    }
-    return 0;
-}
-
 void SystemTray::updateSize()
 {
     // Just ask our parent's layout to give us an appropriate size
Index: systemtray.h
===================================================================
--- systemtray.h	(revision 743900)
+++ systemtray.h	(working copy)
@@ -27,7 +27,6 @@
 #include "systemtraywidget.h"
 
 // Qt
-#include <QGraphicsScene>
 #include <QGraphicsView>
 #include <QPointer>
 
@@ -51,16 +50,13 @@ protected:
 
 private slots:
     void updateSize();
-    void handleSceneChange(const QList<QRectF> &region);
 
 private:
-    bool intersectsRegion(const QList<QRectF> &region);
-    QGraphicsView * findView();
+    void updateWidgetGeometry();
     void updateWidgetOrientation();
 
-    // These can all be deleted externally so we guard them
+    // These can be deleted externally so we guard them
     QPointer<SystemTrayWidget> m_systemTrayWidget;
-    QPointer<QGraphicsScene> m_currentScene;
     QPointer<QGraphicsView> m_currentView;
 };
 


_______________________________________________
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