[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 7:56:55
Message-ID: 200711241656.55746.jasonbstubbs () gmail ! com
[Download RAW message or body]
On Saturday 24 November 2007 03:59:33 Aaron J. Seigo wrote:
> On Friday 23 November 2007, 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.
>
> so the next step is to make the widget a child of the corresponding view,
> and then coordinate between the applet and the view, correct?
The last patch was doing that already, although this one handles a scene
change better by reparenting correctly.
> > Finally, there's a updateGeometry() after resize() in updateSize() that I
> > don't think is strictly needed. Without it, however, the item's size
> > would change but its position wouldn't.
>
> correct, resize doesn't affect positioning, just size. if the item is in a
> layout, however, it should trigger a relayout which may shifts position. if
> that isn't happening, there is a bug in the given layout.
There would seem to be a bug in BoxLayout then.
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.
--
Jason Stubbs
["systray.patch" (text/x-diff)]
Index: systemtray.cpp
===================================================================
--- systemtray.cpp (revision 740589)
+++ systemtray.cpp (working copy)
@@ -21,64 +21,122 @@
// 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());
-}
-
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()
+{
+ // 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 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
@@ -35,19 +41,20 @@
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;
+ // 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