Hi,

I've been working on some improvements to the desktop containment concerning 
applet positioning/transformations. I think the code is ready to be merged 
into trunk. The branches are in svn under branches/work/plasma-desktoplayout. 
I'm also attaching the patches.
Can I merge the code, or does anyone have objections/questions?

To sum up the changes:
- Get rid of the QGraphicsLayout interface from DesktopLayout as it is 
unneeded and not designed for we're doing. A layout is suppose to be the only 
thing manipulating the children, which is not our case (user actions, applets 
resizing themselves).
- Added signals appletTransformedByUsed and appletTransformedItself to 
Plasma::Applet. This makes it easier for the layout code to know when applets 
are transformed (moved/resized/rotated), and it can even know whether the 
change was by the user or the applet itself. Fixes bug 180857. This is needed 
for fixing bug 181841.
- Updated the desktop containment to use these signals.
- Added proper handling of applets resizing themselves to DesktopLayout. E.g. 
if an applet gets larger, applets on its right that are in the way are pushed 
not to overlap.
- Added support for understanding rotations (bug 177152).
- Added emitting of the signals mentioned to some of the applets when they 
resize themselves; more are needed.
Index: applet.h
===================================================================
--- applet.h	(.../trunk/KDE/kdelibs/plasma)	(revision 922977)
+++ applet.h	(.../branches/work/plasma-desktoplayout/kdelibs-plasma)	(revision 922977)
@@ -591,6 +591,16 @@
         void geometryChanged();
 
         /**
+         * Emitted when the user completes a transformation of the applet.
+         */
+        void appletTransformedByUser();
+
+        /**
+         * Emitted when the applet changes its own geometry or transform.
+         */
+        void appletTransformedItself();
+
+        /**
          * Emitted by Applet subclasses when they change a sizeHint and wants to announce the change
          */
         void sizeHintChanged(Qt::SizeHint which);
Index: popupapplet.cpp
===================================================================
--- popupapplet.cpp	(.../trunk/KDE/kdelibs/plasma)	(revision 922977)
+++ popupapplet.cpp	(.../branches/work/plasma-desktoplayout/kdelibs-plasma)	(revision 922977)
@@ -246,6 +246,7 @@
             //size not saved/invalid size saved
             if (oldSize.width() < q->minimumSize().width() || oldSize.height() < q->minimumSize().height()) {
                 q->resize(prefSize);
+                emit q->appletTransformedItself();
             }
 
             //FIXME: this will be automatically propagated by the qgraphicslayout in the future
Index: private/applethandle.cpp
===================================================================
--- private/applethandle.cpp	(.../trunk/KDE/kdelibs/plasma)	(revision 922977)
+++ private/applethandle.cpp	(.../branches/work/plasma-desktoplayout/kdelibs-plasma)	(revision 922977)
@@ -72,6 +72,9 @@
                              Theme::defaultTheme()->colorScheme());
     m_gradientColor = colorScheme.background(KColorScheme::NormalBackground).color();
 
+    m_originalGeom = m_applet->geometry();
+    m_originalTransform = m_applet->transform();
+
     QTransform originalMatrix = m_applet->transform();
     m_applet->resetTransform();
 
@@ -167,6 +170,10 @@
 
     m_applet->setZValue(m_zValue);
 
+    if (m_applet->geometry() != m_originalGeom || m_applet->transform() != m_originalTransform) {
+        emit m_applet->appletTransformedByUser();
+    }
+
     m_applet->update(); // re-render the background, now we've transformed the applet
 
     m_applet = 0;
Index: private/applethandle_p.h
===================================================================
--- private/applethandle_p.h	(.../trunk/KDE/kdelibs/plasma)	(revision 922977)
+++ private/applethandle_p.h	(.../branches/work/plasma-desktoplayout/kdelibs-plasma)	(revision 922977)
@@ -127,6 +127,8 @@
         QPointF m_entryPos; //where the hover in event occurred
         QPointF m_pos;      //current position of applet in sceneCoords
         qreal m_zValue;     //current zValue of the applet, so it can be restored after drag.
+        QRectF m_originalGeom;
+        QTransform m_originalTransform;
 
         // used for both resize and rotate
         QPointF m_origAppletCenter;
Index: containments/desktop/desktop.h
===================================================================
--- containments/desktop/desktop.h	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/desktop.h	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -78,7 +78,8 @@
 
     void onAppletAdded(Plasma::Applet *, const QPointF &);
     void onAppletRemoved(Plasma::Applet *);
-    void onAppletGeometryChanged();
+    void onAppletTransformedByUser();
+    void onAppletTransformedItself();
     void refreshWorkingArea();
 
 private:
Index: containments/desktop/itemspace.h
===================================================================
--- containments/desktop/itemspace.h	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/itemspace.h	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -42,8 +42,6 @@
 
     void setWorkingArea(QSizeF area);
 
-    void activate();
-
     /**
      * Returns the visibility of an item at a given position.
      * This is the part of the item inside the working area.
@@ -53,7 +51,7 @@
     class ItemSpaceItem
     {
       public:
-        QRectF preferredGeometry;
+        QPointF preferredPosition;
         QRectF lastGeometry;
         bool pushBack : 1;
         bool animateMovement : 1;
@@ -76,11 +74,6 @@
     Q_DECLARE_FLAGS(PushPower, PushPowerFlag)
 
     /**
-     * Offset the positions of all items.
-     **/
-    void offsetPositions(const QPointF &offset);
-
-    /**
      * Push an item group. Requires no initialization.
      *
      * @param groupId the index of the group
@@ -110,14 +103,29 @@
     void removeItem(int groupIndex, int itemInGroup);
 
     /**
-     * Updates groups to reflect the item's geometry. 
+     * Move the item to a new position.
      *
      * @param groupIndex the index of the item's group
      * @param itemInGroup the index of the item in its group
+     * @param newGeom the new geometry of the item
      **/
-    void updateItem(int groupIndex, int itemInGroup);
+    void moveItem(int groupIndex, int itemInGroup, QRectF newGeom);
 
     /**
+     * Resize an item. The item's alignment corner will be the center of resizing.
+     *
+     * @param groupId the index of the group
+     * @param direction in which direction pushing will be done
+     * @param newSize the item's new size
+     **/
+    void resizeItem(int groupId, int itemInGroup, QSizeF newSize);
+
+    /**
+     * Offset the positions of all items.
+     **/
+    void offsetPositions(const QPointF &offset);
+
+    /**
      * Find an item by its number as if we iterated over
      * all groups and over all items in each group.
      **/
@@ -129,18 +137,6 @@
     bool locateItemByUser(QVariant user, int *groupIndex, int *itemInGroup) const;
 
     /**
-     * Prepare for pushing.
-     * After that, move requests can be posted to item groups
-     * with ItemGroup::addRequest and the move can be performed
-     * with ItemGroup::applyResults.
-     *
-     * @param direction in which direction pushing will be done
-     * @param power how 'powerful' the push is; what types of obstacles
-     *              can be pushed or ignored
-     **/
-    void preparePush(Direction direction, PushPower power);
-
-    /**
      * Finds an empty place for an item.
      * Tries to stack the item vertically, starting in the corner
      * of alignment, and advances horizontally once no more positions
@@ -283,6 +279,31 @@
 
   private:
 
+    void linkItem(ItemSpaceItem newItem);
+    void unlinkItem(int removeGroup, int removeItemInGroup);
+
+    /**
+     * Prepare for pushing.
+     * After that, move requests can be posted to item groups
+     * with ItemGroup::addRequest and the move can be performed
+     * with ItemGroup::applyResults.
+     *
+     * @param direction in which direction pushing will be done
+     * @param power how 'powerful' the push is; what types of obstacles
+     *              can be pushed or ignored
+     **/
+    void preparePush(Direction direction, PushPower power);
+
+    /**
+     * Look for items overlapping with working area borders and move them inside as much as possible.
+     **/
+    void checkBorders();
+
+    /**
+     * Look for items not in their preferred positions and move them back as much as possible.
+     **/
+    void checkPreferredPositions();
+
     QRectF itemInRegionStartingFirstVert(const QRectF &region) const;
     QRectF itemInRegionEndingLastVert(const QRectF &region) const;
     QRectF itemInRegionEndingFirstHoriz(const QRectF &region) const;
Index: containments/desktop/desktoplayout.cpp
===================================================================
--- containments/desktop/desktoplayout.cpp	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/desktoplayout.cpp	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -20,43 +20,47 @@
 
 #include "desktoplayout.h"
 
-DesktopLayout::DesktopLayout(QGraphicsLayoutItem *parent)
+DesktopLayout::DesktopLayout()
       : QObject(0),
-        QGraphicsLayout(parent),
-        autoWorkingArea(true),
         temporaryPlacement(false),
-        visibilityTolerance(0),
-        m_activated(false)
+        visibilityTolerance(0)
 {
     connect(Plasma::Animator::self(), SIGNAL(movementFinished(QGraphicsItem*)),
             this, SLOT(movementFinished(QGraphicsItem*)));
 }
 
-void DesktopLayout::addItem(QGraphicsLayoutItem *item, bool pushBack, const QRectF &preferredGeom, const QRectF &lastGeom)
+void DesktopLayout::addItem(QGraphicsWidget *item, bool pushBack, bool position)
 {
     int key = newItemKey();
 
+    QRectF logicalGeom;
+    QTransform revertTransform;
+    getItemInstantRelativeGeometry(item, logicalGeom, revertTransform);
+
+    //kDebug() << "addItem position" << position << "logical" << logicalGeom;
+
+    if (position) {
+        logicalGeom = positionNewItem(logicalGeom.size());
+    }
+
     ItemSpace::ItemSpaceItem spaceItem;
     spaceItem.pushBack = pushBack;
     spaceItem.animateMovement = false;
-    spaceItem.preferredGeometry = preferredGeom;
-    spaceItem.lastGeometry = (lastGeom.isValid() ? lastGeom : preferredGeom);
+    spaceItem.preferredPosition = logicalGeom.topLeft();
+    spaceItem.lastGeometry = logicalGeom;
     spaceItem.user = QVariant(key);
 
     DesktopLayoutItem desktopItem;
     desktopItem.item = item;
     desktopItem.temporaryGeometry = QRectF(0, 0, -1, -1);
+    desktopItem.revertTransform = revertTransform;
 
     itemSpace.addItem(spaceItem);
     items.insert(key, desktopItem);
-
-    invalidate();
 }
 
-void DesktopLayout::addItem(QGraphicsLayoutItem *item, bool pushBack, const QSizeF &size)
+QRectF DesktopLayout::positionNewItem(QSizeF itemSize)
 {
-    QSizeF itemSize = ( size.isValid() ? size : item->effectiveSizeHint(Qt::PreferredSize) );
-
     // get possible positions
     QList<QPointF> possiblePositions = itemSpace.positionVertically(itemSize, itemSpace.spaceAlignment, false, true);
     //kDebug() << "possiblePositions" << possiblePositions;
@@ -77,20 +81,19 @@
         qreal bestVisibility = 0;
         foreach (const QPointF &position, possiblePositions) {
             // see how much the item can be pushed into the working area:
-            // copy our ItemSpace, add the item to the copy, activate it
+            // copy our ItemSpace, add the item to the copy
             // and check the resulting position's visibility
 
             ItemSpace tempItemSpace(itemSpace);
 
             ItemSpace::ItemSpaceItem spaceItem;
-            spaceItem.pushBack = pushBack;
+            spaceItem.pushBack = false;
             spaceItem.animateMovement = false;
-            spaceItem.preferredGeometry = QRectF(position, itemSize);
+            spaceItem.preferredPosition = position;
             spaceItem.lastGeometry = QRectF(position, itemSize);
             spaceItem.user = QVariant(-1);
 
             tempItemSpace.addItem(spaceItem);
-            tempItemSpace.activate();
             int tempGroup, tempItem;
             tempItemSpace.locateItemByUser(QVariant(-1), &tempGroup, &tempItem);
 
@@ -115,8 +118,9 @@
         bestGeometry = QRectF(bestPosition, itemSize);
     }
 
-    addItem(item, pushBack, bestGeometry);
     kDebug() << "Positioned item to" << bestGeometry;
+
+    return bestGeometry;
 }
 
 bool DesktopLayout::getPushBack(int index)
@@ -128,13 +132,13 @@
     return itemSpace.m_groups[group].m_groupItems[item].pushBack;
 }
 
-QRectF DesktopLayout::getPreferredGeometry(int index)
+QPointF DesktopLayout::getPreferredPosition(int index)
 {
     int group;
     int item;
     itemSpace.locateItemByPosition(index, &group, &item);
 
-    return itemSpace.m_groups[group].m_groupItems[item].preferredGeometry;
+    return itemSpace.m_groups[group].m_groupItems[item].preferredPosition;
 }
 
 QRectF DesktopLayout::getLastGeometry(int index)
@@ -154,7 +158,6 @@
 void DesktopLayout::setScreenSpacing(qreal spacing)
 {
     itemSpace.screenSpacing = spacing;
-    invalidate();
 }
 
 void DesktopLayout::setShiftingSpacing(qreal spacing)
@@ -166,7 +169,6 @@
 void DesktopLayout::setVisibilityTolerance(qreal part)
 {
     visibilityTolerance = part;
-    invalidate();
 }
 
 void DesktopLayout::setWorkingArea(QRectF area)
@@ -176,32 +178,24 @@
     itemSpace.offsetPositions(workingStart - area.topLeft());
     itemSpace.setWorkingArea(area.size());
     workingStart = area.topLeft();
-    invalidate();
 }
 
 void DesktopLayout::setAlignment(Qt::Alignment alignment)
 {
     itemSpace.spaceAlignment = alignment;
-    invalidate();
 }
 
 void DesktopLayout::setTemporaryPlacement(bool enabled)
 {
     temporaryPlacement = enabled;
-    invalidate();
 }
 
-void DesktopLayout::setAutoWorkingArea (bool value)
-{
-    autoWorkingArea = value;
-}
-
 int DesktopLayout::count () const
 {
     return items.size();
 }
 
-QGraphicsLayoutItem *DesktopLayout::itemAt (int i) const
+QGraphicsWidget *DesktopLayout::itemAt (int i) const
 {
     int group = -2, item = -2;
     itemSpace.locateItemByPosition(i, &group, &item);
@@ -220,8 +214,6 @@
     itemSpace.removeItem(group, item);
     // remove from local list
     items.remove(itemKey);
-
-    invalidate();
 }
 
 void DesktopLayout::performTemporaryPlacement(int group, int itemInGroup)
@@ -240,7 +232,8 @@
 
     spaceItem.lastGeometry = origGeom;
     item.temporaryGeometry = QRectF(newPos, origGeom.size());
-    item.item->setGeometry(item.temporaryGeometry.translated(workingStart));
+
+    item.item->setGeometry(geometryRelativeToAbsolute(spaceItem.user.toInt(), item.temporaryGeometry));
 }
 
 void DesktopLayout::revertTemporaryPlacement(int group, int itemInGroup)
@@ -249,22 +242,52 @@
     DesktopLayoutItem &item = items[spaceItem.user.toInt()];
 
     item.temporaryGeometry = QRectF();
-    item.item->setGeometry(spaceItem.lastGeometry.translated(workingStart));
+
+    item.item->setGeometry(geometryRelativeToAbsolute(spaceItem.user.toInt(), spaceItem.lastGeometry));
 }
 
-// update anything that needs updating
-void DesktopLayout::setGeometry(const QRectF &rect)
+QRectF DesktopLayout::transformRect(const QRectF &rect, const QTransform &transform)
 {
-    m_activated = true;
-    QGraphicsLayout::setGeometry(rect);
+    QTransform t;
+    t.translate(rect.left(), rect.top());
+    t = transform * t;
+    t.translate(-rect.left(), -rect.top());
+    return t.mapRect(rect);
+}
 
-    if (autoWorkingArea || !itemSpace.workingGeom.isValid()) {
-        setWorkingArea(rect);
+void DesktopLayout::getItemInstantRelativeGeometry(QGraphicsWidget *item, QRectF &outGeometry, QTransform &outRevertTransform)
+{
+    QRectF origGeom = item->geometry();
+
+    QTransform transform;
+    if (item->transform().m11() && item->transform().m22()) {
+        transform = item->transform();
     }
 
-    // activate the ItemSpace to perform motion as needed
-    itemSpace.activate();
+    QRectF transformedAbsoluteGeom = transformRect(origGeom, transform);
+    QRectF logicalGeom = transformedAbsoluteGeom.translated(-workingStart);
 
+    QPointF positionDiff = origGeom.topLeft() - transformedAbsoluteGeom.topLeft();
+    qreal scaleDiffX = origGeom.width() / transformedAbsoluteGeom.width();
+    qreal scaleDiffY = origGeom.height() / transformedAbsoluteGeom.height();
+
+    QTransform r;
+    r.translate(positionDiff.x(), positionDiff.y());
+    r.scale(scaleDiffX, scaleDiffY);
+
+    outGeometry = logicalGeom;
+    outRevertTransform = r;
+}
+
+QRectF DesktopLayout::geometryRelativeToAbsolute(int itemKey, const QRectF &relative)
+{
+    QRectF translated = relative.translated(workingStart);
+    QRectF detransformed = transformRect(translated, items[itemKey].revertTransform);
+    return detransformed;
+}
+
+void DesktopLayout::adjustPhysicalPositions()
+{
     for (int groupId = 0; groupId < itemSpace.m_groups.size(); groupId++) {
         ItemSpace::ItemGroup &group = itemSpace.m_groups[groupId];
 
@@ -272,8 +295,8 @@
             ItemSpace::ItemSpaceItem &spaceItem = group.m_groupItems[itemId];
             DesktopLayoutItem &desktopItem = items[spaceItem.user.toInt()];
 
-            //  Temporarily place the item if it could not be pushed inside the working area.
-            //  Put it back if it fits again.
+            // Temporarily place the item if it could not be pushed inside the working area.
+            // Put it back if it fits again.
             if (itemSpace.positionVisibility(spaceItem.lastGeometry) < visibilityTolerance) {
                 performTemporaryPlacement(groupId, itemId);
             } else if (desktopItem.temporaryGeometry.isValid()) {
@@ -281,22 +304,23 @@
             }
 
             // Reset the absolute position if needed
-            QRectF visibleGeom = (desktopItem.temporaryGeometry.isValid() ? desktopItem.temporaryGeometry : spaceItem.lastGeometry);
-            QRectF absoluteGeom = visibleGeom.translated(workingStart);
+
+            QRectF effectiveGeom = (desktopItem.temporaryGeometry.isValid() ? desktopItem.temporaryGeometry : spaceItem.lastGeometry);
+            QRectF absoluteGeom = geometryRelativeToAbsolute(spaceItem.user.toInt(), effectiveGeom);
+
             if (desktopItem.item->geometry() != absoluteGeom) {
-                QGraphicsWidget *w = dynamic_cast<QGraphicsWidget*>(desktopItem.item);
-                if (w && spaceItem.animateMovement)  {
+                if (spaceItem.animateMovement)  {
                     Plasma::Animator *anim = Plasma::Animator::self();
-                    bool animating = m_animatingItems.contains(w);
+                    bool animating = m_animatingItems.contains(desktopItem.item);
                     if (animating) {
-                        anim->stopItemMovement(m_animatingItems.value(w));
+                        anim->stopItemMovement(m_animatingItems.value(desktopItem.item));
                     }
-                    int id = Plasma::Animator::self()->moveItem(w, Plasma::Animator::FastSlideInMovement,
+                    int id = Plasma::Animator::self()->moveItem(desktopItem.item, Plasma::Animator::FastSlideInMovement,
                                                                 absoluteGeom.topLeft().toPoint());
                     if (id > 0) {
-                        m_animatingItems.insert(w, id);
+                        m_animatingItems.insert(desktopItem.item, id);
                     } else if (animating) {
-                        m_animatingItems.remove(w);
+                        m_animatingItems.remove(desktopItem.item);
                     }
 
                     spaceItem.animateMovement = false;
@@ -306,17 +330,10 @@
             }
         }
     }
-    m_activated = false;
 }
 
-// This should be called when the geometry of an item has been changed.
-// If the change was made by the user, the new position is used as the preferred position.
-void DesktopLayout::itemGeometryChanged(QGraphicsLayoutItem *layoutItem)
+void DesktopLayout::itemTransformed(QGraphicsWidget *layoutItem, ItemTransformType type)
 {
-    if (m_activated || m_animatingItems.contains(dynamic_cast<QGraphicsWidget*>(layoutItem))) {
-        return;
-    }
-
     // get local item key
     int itemKey = -1;
     QMapIterator<int, DesktopLayoutItem> i(items);
@@ -336,23 +353,21 @@
     itemSpace.locateItemByUser(itemKey, &group, &item);
     ItemSpace::ItemSpaceItem &spaceItem = itemSpace.m_groups[group].m_groupItems[item];
 
-    QRectF currentRelative = layoutItem->geometry().translated(-workingStart);
-    if (spaceItem.lastGeometry != currentRelative) {
-        spaceItem.lastGeometry = currentRelative;
-        spaceItem.preferredGeometry = currentRelative;
+    QRectF logicalGeom;
+    QTransform revertTransform;
+    getItemInstantRelativeGeometry(layoutItem, logicalGeom, revertTransform);
 
-        itemSpace.updateItem(group, item);
-        invalidate();
+    if (type == ItemTransformSelf) {
+        // don't care about the new position, just update the size
+        itemSpace.resizeItem(group, item, logicalGeom.size());
     }
+    else if (spaceItem.lastGeometry != logicalGeom) {
+        // use the new geometry as the preferred
+        itemSpace.moveItem(group, item, logicalGeom);
+    }
+    items[itemKey].revertTransform = revertTransform;
 }
 
-QSizeF DesktopLayout::sizeHint (Qt::SizeHint which, const QSizeF &constraint) const
-{
-    Q_UNUSED(which)
-    Q_UNUSED(constraint)
-    return QSizeF();
-}
-
 void DesktopLayout::movementFinished(QGraphicsItem* item)
 {
     if (m_animatingItems.contains(item)) {
Index: containments/desktop/desktop.cpp
===================================================================
--- containments/desktop/desktop.cpp	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/desktop.cpp	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -68,20 +68,14 @@
 {
     qRegisterMetaType<QImage>("QImage");
     qRegisterMetaType<QPersistentModelIndex>("QPersistentModelIndex");
-    connect(this, SIGNAL(appletAdded(Plasma::Applet *, const QPointF &)),
-            this, SLOT(onAppletAdded(Plasma::Applet *, const QPointF &)));
-    connect(this, SIGNAL(appletRemoved(Plasma::Applet *)),
-            this, SLOT(onAppletRemoved(Plasma::Applet *)));
 
     m_layout = new DesktopLayout;
-    m_layout->setAutoWorkingArea(false);
     m_layout->setAlignment(Qt::AlignTop|Qt::AlignLeft);
     m_layout->setPlacementSpacing(20);
     m_layout->setScreenSpacing(5);
     m_layout->setShiftingSpacing(0);
     m_layout->setTemporaryPlacement(true);
     m_layout->setVisibilityTolerance(0.5);
-    setLayout(m_layout);
 
     resize(800, 600);
 
@@ -106,6 +100,19 @@
         connect(corona(), SIGNAL(availableScreenRegionChanged()),
                 this, SLOT(refreshWorkingArea()));
         refreshWorkingArea();
+
+        connect(this, SIGNAL(appletAdded(Plasma::Applet *, const QPointF &)),
+                this, SLOT(onAppletAdded(Plasma::Applet *, const QPointF &)));
+        connect(this, SIGNAL(appletRemoved(Plasma::Applet *)),
+                this, SLOT(onAppletRemoved(Plasma::Applet *)));
+
+        foreach (Applet *applet, applets()) {
+            m_layout->addItem(applet, true, false);
+            connect(applet, SIGNAL(appletTransformedByUser()), this, SLOT(onAppletTransformedByUser()));
+            connect(applet, SIGNAL(appletTransformedItself()), this, SLOT(onAppletTransformedItself()));
+        }
+
+        m_layout->adjustPhysicalPositions();
     }
 }
 
@@ -251,19 +258,16 @@
 {
     if (dropping || pos != QPointF(-1,-1) || applet->geometry().topLeft() != QPointF(0,0)) {
         // add item to the layout using the current position
-        m_layout->addItem(applet, true, applet->geometry());
+        m_layout->addItem(applet, true, false);
     } else {
-        /*
-            There seems to be no uniform way to get the applet's preferred size.
-            Regular applets properly set their current size when created, but report useless size hints.
-            Proxy widget applets don't set their size properly, but report valid size hints. However, they
-            will obtain proper size if we just re-set the geometry to the current value.
-        */
-        applet->setGeometry(applet->geometry());
-        m_layout->addItem(applet, true, applet->geometry().size());
+        // auto-position
+        m_layout->addItem(applet, true, true);
     }
 
-    connect(applet, SIGNAL(geometryChanged()), this, SLOT(onAppletGeometryChanged()));
+    m_layout->adjustPhysicalPositions();
+
+    connect(applet, SIGNAL(appletTransformedByUser()), this, SLOT(onAppletTransformedByUser()));
+    connect(applet, SIGNAL(appletTransformedItself()), this, SLOT(onAppletTransformedItself()));
 }
 
 void DefaultDesktop::onAppletRemoved(Plasma::Applet *applet)
@@ -271,16 +275,24 @@
     for (int i=0; i < m_layout->count(); i++) {
         if (applet == m_layout->itemAt(i)) {
             m_layout->removeAt(i);
+            m_layout->adjustPhysicalPositions();
             return;
         }
     }
 }
 
-void DefaultDesktop::onAppletGeometryChanged()
+void DefaultDesktop::onAppletTransformedByUser()
 {
-    m_layout->itemGeometryChanged((Applet *)sender());
+    m_layout->itemTransformed((Applet *)sender(), DesktopLayout::ItemTransformUser);
+    m_layout->adjustPhysicalPositions();
 }
 
+void DefaultDesktop::onAppletTransformedItself()
+{
+    m_layout->itemTransformed((Applet *)sender(), DesktopLayout::ItemTransformSelf);
+    m_layout->adjustPhysicalPositions();
+}
+
 void DefaultDesktop::refreshWorkingArea()
 {
     Corona *c = corona();
@@ -305,6 +317,7 @@
     if (workingGeom != QRectF()) {
         //kDebug() << "!!!!!!!!!!!!! workingGeom is" << workingGeom;
         m_layout->setWorkingArea(workingGeom);
+        m_layout->adjustPhysicalPositions();
     }
 }
 
Index: containments/desktop/itemspace.cpp
===================================================================
--- containments/desktop/itemspace.cpp	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/itemspace.cpp	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -33,10 +33,19 @@
             offsetPositions(QPointF(area.width()-workingGeom.width(), area.height()-workingGeom.height()));
         }
     }
+    QSizeF old = workingGeom;
     workingGeom = area;
+
+    if (area.width() < old.width() || area.height() < old.height()) {
+        checkBorders();
+    }
+
+    if (area.width() > old.width() || area.height() > old.height()) {
+        checkPreferredPositions();
+    }
 }
 
-void ItemSpace::activate()
+void ItemSpace::checkBorders()
 {
     for (int groupId = 0; groupId < m_groups.size(); groupId++) {
         ItemGroup &group = m_groups[groupId];
@@ -96,22 +105,37 @@
                 }
                 performPush(groupId, DirUp, push, power);
             }
+        }
+    }
+}
 
+void ItemSpace::checkPreferredPositions()
+{
+    for (int groupId = 0; groupId < m_groups.size(); groupId++) {
+        ItemGroup &group = m_groups[groupId];
+
+        for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
+            ItemSpaceItem &item = group.m_groupItems[itemId];
+
+            qreal push;
+            PushPower power;
+
             /*
               Push items back towards their perferred positions.
               Cannot push items out of the working area,
               cannot push items away from their preferred positions.
             */
             if (item.pushBack) {
+                QRectF preferredGeometry = QRectF(item.preferredPosition, item.lastGeometry.size());
                 // left/right
-                push = item.preferredGeometry.left() - item.lastGeometry.left();
+                push = preferredGeometry.left() - item.lastGeometry.left();
                 if (push > 0) {
                     performPush(groupId, DirRight, push, NoPower);
                 } else if (push < 0) {
                     performPush(groupId, DirLeft, -push, NoPower);
                 }
                 // up/down
-                push = item.preferredGeometry.top() - item.lastGeometry.top();
+                push = preferredGeometry.top() - item.lastGeometry.top();
                 if (push > 0) {
                     performPush(groupId, DirDown, push, NoPower);
                 } else if (push < 0) {
@@ -139,7 +163,7 @@
         for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
             ItemSpaceItem &item = group.m_groupItems[itemId];
 
-            item.preferredGeometry.adjust(offset.x(), offset.y(), offset.x(), offset.y());
+            item.preferredPosition += offset;
             item.lastGeometry.adjust(offset.x(), offset.y(), offset.x(), offset.y());
         }
     }
@@ -423,19 +447,20 @@
 
         // limit push to not push the item away from its preferred position
         if (!(itemSpace->m_power & PushAwayFromPreferred) && item.pushBack) {
+            QRectF preferredGeometry = QRectF(item.preferredPosition, item.lastGeometry.size());
             qreal limit;
             switch (itemSpace->m_direction) {
                 case DirLeft:
-                    limit = origGeom.left() - item.preferredGeometry.left();
+                    limit = origGeom.left() - preferredGeometry.left();
                     break;
                 case DirRight:
-                    limit = -(origGeom.left() - item.preferredGeometry.left());
+                    limit = -(origGeom.left() - preferredGeometry.left());
                     break;
                 case DirUp:
-                    limit = origGeom.top() - item.preferredGeometry.top();
+                    limit = origGeom.top() - preferredGeometry.top();
                     break;
                 case DirDown:
-                    limit = -(origGeom.top() - item.preferredGeometry.top());
+                    limit = -(origGeom.top() - preferredGeometry.top());
                     break;
             }
             limit = qMax(qreal(0.0), limit);
@@ -592,7 +617,7 @@
 }
 
 // TODO: optimize
-void ItemSpace::addItem(ItemSpaceItem newItem)
+void ItemSpace::linkItem(ItemSpaceItem newItem)
 {
     QList<ItemSpaceItem> newGroupItems;
     QRectF newItemGeom = newItem.lastGeometry.adjusted(-shiftingSpacing, -shiftingSpacing,
@@ -628,7 +653,7 @@
 }
 
 // TODO: optimize
-void ItemSpace::removeItem(int removeGroup, int removeItemInGroup)
+void ItemSpace::unlinkItem(int removeGroup, int removeItemInGroup)
 {
     // remove items from group
     m_groups[removeGroup].m_groupItems.removeAt(removeItemInGroup);
@@ -638,18 +663,199 @@
     m_groups.removeAt(removeGroup);
     // re-add other group items
     foreach (const ItemSpaceItem &item, otherGroupItems) {
-        addItem(item);
+        linkItem(item);
     }
 }
 
+void ItemSpace::addItem(ItemSpaceItem newItem)
+{
+    linkItem(newItem);
+    checkBorders();
+}
+
+void ItemSpace::removeItem(int removeGroup, int removeItemInGroup)
+{
+    unlinkItem(removeGroup, removeItemInGroup);
+    checkPreferredPositions();
+}
+
 // TODO: optimize
-void ItemSpace::updateItem(int group, int itemInGroup)
+void ItemSpace::moveItem(int groupIndex, int itemInGroup, QRectF newGeom)
 {
-    ItemSpaceItem copy = m_groups[group].m_groupItems[itemInGroup];
-    removeItem(group, itemInGroup);
-    addItem(copy);
+    ItemSpaceItem copy = m_groups[groupIndex].m_groupItems[itemInGroup];
+
+    unlinkItem(groupIndex, itemInGroup);
+
+    copy.preferredPosition = newGeom.topLeft();
+    copy.lastGeometry = newGeom;
+    linkItem(copy);
+
+    checkBorders();
+    checkPreferredPositions();
 }
 
+void ItemSpace::resizeItem(int resizeGroupId, int resizeItemInGroup, QSizeF newSize)
+{
+    ItemSpaceItem &resizeItem = m_groups[resizeGroupId].m_groupItems[resizeItemInGroup];
+    QRectF oldGeom = resizeItem.lastGeometry;
+
+    // the alignment corner on the applet is the center of resizing, meaning that it won't move
+    // calculate new geometry
+    QPointF newPos;
+    if ((spaceAlignment & Qt::AlignLeft)) {
+        newPos.rx() = oldGeom.left();
+    } else {
+        newPos.rx() = oldGeom.right() - newSize.width();
+    }
+    if ((spaceAlignment & Qt::AlignTop)) {
+        newPos.ry() = oldGeom.top();
+    } else {
+        newPos.ry() = oldGeom.bottom() - newSize.height();
+    }
+
+    QRectF newGeom = QRectF(newPos, newSize);
+
+    kDebug() << "Resizing" << oldGeom << "to" << newGeom;
+
+    for (int groupId = 0; groupId < m_groups.size(); groupId++) {
+        ItemGroup &group = m_groups[groupId];
+
+        for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
+            ItemSpaceItem &item = group.m_groupItems[itemId];
+
+            if (groupId == resizeGroupId) {
+                // Items in our group
+                // We must make sure to preserve the group, so that when a reverse resize is done,
+                // all items are in the original state.
+
+                // TODO: Implement pushing group items. To do that, we have to seperate this item
+                // from other group items.
+                // Currently, group items are not moved. Shrinking may leave group items physically
+                // disconnected from this item.f
+            } else {
+                // Check if the item is in the way.
+                if (!newGeom.intersects(item.lastGeometry)) continue;
+
+                // Calculate on which edges we collide with this item, how much
+                // it would have to be pushed, and on what part of the way we collide.
+
+                bool collidedRight = false;
+                bool collidedLeft = false;
+                bool collidedBottom = false;
+                bool collidedTop = false;
+
+                qreal pushRight = 0;
+                qreal pushLeft = 0;
+                qreal pushTop = 0;
+                qreal pushBottom = 0;
+
+                qreal collisionTimeRight = 0;
+                qreal collisionTimeLeft = 0;
+                qreal collisionTimeBottom = 0;
+                qreal collisionTimeTop = 0;
+
+                if (newGeom.right() > oldGeom.right() && item.lastGeometry.left() >= oldGeom.right() && item.lastGeometry.left() < newGeom.right()) {
+                    collidedRight = true;
+                    pushRight = newGeom.right() - item.lastGeometry.left();
+                    collisionTimeRight = (item.lastGeometry.left() - oldGeom.right()) / (newGeom.right() - oldGeom.right());
+                }
+                else if (newGeom.left() < oldGeom.left() && item.lastGeometry.right() <= oldGeom.left() && item.lastGeometry.right() < newGeom.left()) {
+                    collidedLeft = true;
+                    pushLeft = item.lastGeometry.right() - newGeom.left();
+                    collisionTimeLeft = (oldGeom.left() - item.lastGeometry.right()) / (newGeom.left() - newGeom.left());
+                }
+
+                if (newGeom.bottom() > oldGeom.bottom() && item.lastGeometry.top() >= oldGeom.bottom() && item.lastGeometry.top() < newGeom.bottom()) {
+                    collidedBottom = true;
+                    pushBottom = newGeom.bottom() - item.lastGeometry.top();
+                    collisionTimeBottom = (item.lastGeometry.top() - oldGeom.bottom()) / (newGeom.bottom() - oldGeom.bottom());
+                }
+                else if (newGeom.top() < oldGeom.top() && item.lastGeometry.bottom() <= oldGeom.top() && item.lastGeometry.bottom() < newGeom.top()) {
+                    collidedTop = true;
+                    pushTop = item.lastGeometry.bottom() - newGeom.top();
+                    collisionTimeTop = (oldGeom.top() - item.lastGeometry.bottom()) / (newGeom.top() - newGeom.top());
+                }
+
+                // Determine what direction to push the offending item.
+                // If we would collide with it in two edges, use the
+                // one where we would collide first.
+
+                Direction direction = 0;
+
+                if (collidedRight) {
+                    if (collidedTop && collisionTimeTop < collisionTimeRight) {
+                        direction = DirUp;
+                    }
+                    else if (collidedBottom && collisionTimeBottom < collisionTimeRight) {
+                        direction = DirDown;
+                    }
+                    else {
+                        direction = DirRight;
+                    }
+                }
+                else if (collidedLeft) {
+                    if (collidedTop && collisionTimeTop < collisionTimeLeft) {
+                        direction = DirUp;
+                    }
+                    else if (collidedBottom && collisionTimeBottom < collisionTimeLeft) {
+                        direction = DirDown;
+                    }
+                    else {
+                        direction = DirLeft;
+                    }
+                }
+                else if (collidedBottom) {
+                    direction = DirDown;
+                }
+                else if (collidedTop) {
+                    direction = DirUp;
+                }
+
+                // finally push the item
+
+                if (direction) {
+                    PushPower power = PushAwayFromPreferred;
+                    qreal push;
+
+                    switch (direction) {
+                    case DirRight:
+                        push = pushRight;
+                        if ((spaceAlignment & Qt::AlignLeft)) {
+                            power |= PushOverBorder;
+                        }
+                        break;
+                    case DirLeft:
+                        push = pushLeft;
+                        if ((spaceAlignment & Qt::AlignRight)) {
+                            power |= PushOverBorder;
+                        }
+                        break;
+                    case DirDown:
+                        push = pushBottom;
+                        if ((spaceAlignment & Qt::AlignTop)) {
+                            power |= PushOverBorder;
+                        }
+                        break;
+                    case DirUp:
+                        push = pushTop;
+                        if ((spaceAlignment & Qt::AlignBottom)) {
+                            power |= PushOverBorder;
+                        }
+                        break;
+                    }
+
+                    performPush(groupId, direction, push, power);
+                }
+            }
+        }
+    }
+
+    resizeItem.lastGeometry = newGeom;
+
+    checkBorders();
+    checkPreferredPositions();
+}
+
 bool ItemSpace::locateItemByPosition(int pos, int *groupIndex, int *itemInGroup) const
 {
     int current = 0;
Index: containments/desktop/desktoplayout.h
===================================================================
--- containments/desktop/desktoplayout.h	(.../trunk/KDE/kdebase/workspace/plasma)	(revision 922978)
+++ containments/desktop/desktoplayout.h	(.../branches/work/plasma-desktoplayout/kdebase-workspace-plasma)	(revision 922978)
@@ -10,43 +10,34 @@
 #ifndef _DESKTOPLAYOUT_H
 #define _DESKTOPLAYOUT_H
 
-#include <QGraphicsLayout>
 #include <QHash>
 #include <QMap>
 #include <QList>
 #include <QObject>
+#include <QTransform>
 
 #include "itemspace.h"
 
-class QGraphicsItem;
-
-class DesktopLayout : public QObject, public QGraphicsLayout
+class DesktopLayout : public QObject
 {
     Q_OBJECT
 
 public:
-    DesktopLayout (QGraphicsLayoutItem *parent = 0);
+    DesktopLayout ();
 
     /**
      * Adds a new item.
-     * The item will be automatically positioned.
      *
      * @param item the item to add
      * @param pushBack if the item should attempt to always be in its preferred position;
      *                 if false, it will only move when pushed by other items or edges
      *                 of the working area
-     * @param size the size initial size of the item; if invalid or not supplied,
-     *             the PreferredSize hint will be used
+     * @param position if the item should be repositioned
      **/
-    void addItem(QGraphicsLayoutItem *item, bool pushBack = true, const QSizeF &size = QSizeF());
+    void addItem(QGraphicsWidget *item, bool pushBack = true, bool position = true);
 
-    /**
-     * Adds a previously managed item.
-     **/
-    void addItem(QGraphicsLayoutItem *item, bool pushBack, const QRectF &preferredGeom, const QRectF &lastGeom = QRectF());
-
     bool getPushBack(int index);
-    QRectF getPreferredGeometry(int index);
+    QPointF getPreferredPosition(int index);
     QRectF getLastGeometry(int index);
 
     /**
@@ -74,20 +65,11 @@
     void setVisibilityTolerance(qreal part);
 
     /**
-     * Sets whether the working area should always be
-     * considered the geometry of the managed widget.
-     * Default is on.
+     * Enables or disables temporary placement.
      **/
-    void setAutoWorkingArea(bool value);
+    void setTemporaryPlacement(bool enabled);
 
     /**
-     * Sets the area of the widget where items can be displayed.
-     * If you turned off auto working area, you have to use
-     * this function to adjust it manually every time it changes.
-     **/
-    void setWorkingArea(QRectF area);
-
-    /**
      * Sets the alignment.
      * This defines the sides of the working area where items are
      * pushed inside in case the working area shrinks.
@@ -97,36 +79,61 @@
     void setAlignment(Qt::Alignment alignment);
 
     /**
-     * Enables or disables temporary placement.
+     * Call this to change the working area.
      **/
-    void setTemporaryPlacement(bool enabled);
+    void setWorkingArea(QRectF area);
 
+    enum ItemTransformType {
+        ItemTransformUser = 1,
+        ItemTransformSelf = 2
+    };
+
     /**
-     * Checks if the specified item's geometry has been changed externally
-     * and sets its preferred position to the current position.
-     * Call this when an item has been manually moved or resized.
+     * Call this when an item has been moved/resized/transformed either by the user or itself.
+     *
+     * @param item the item affected
+     * @param type whether the change was by the user (ItemTransformUser) or the applet itself (ItemTransformSelf)
      **/
-    void itemGeometryChanged(QGraphicsLayoutItem *layoutItem);
+    void itemTransformed(QGraphicsWidget *item, ItemTransformType type);
 
-    // inherited from QGraphicsLayout
+    /**
+     * Adjusts the items' on-screen positions to match calculations.
+     **/
+    void adjustPhysicalPositions();
+
+    /**
+     * Returns the count of items in the layout.
+     **/
     int count() const;
-    QGraphicsLayoutItem *itemAt(int index) const;
+
+    /**
+     * Returns the applet at the specified position.
+     **/
+    QGraphicsWidget *itemAt(int index) const;
+
+    /**
+     * Removes the item at the specified position.
+     * The ordering of remaining items after removing one is undefined.
+     **/
     void removeAt(int index);
 
-    // inherited from QGraphicsLayoutItem
-    void setGeometry(const QRectF &rect);
-    QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const;
-
 private slots:
-    void movementFinished(QGraphicsItem*);
+    void movementFinished(QGraphicsItem *);
 
 private:
 
+    QRectF positionNewItem(QSizeF itemSize);
+
+    QRectF transformRect(const QRectF &rect, const QTransform &transform);
+    void getItemInstantRelativeGeometry(QGraphicsWidget *item, QRectF &outGeometry, QTransform &outRevertTransform);
+    QRectF geometryRelativeToAbsolute(int itemKey, const QRectF &relative);
+
     class DesktopLayoutItem
     {
       public:
-        QGraphicsLayoutItem *item;
+        QGraphicsWidget *item;
         QRectF temporaryGeometry;
+        QTransform revertTransform;
     };
 
     int newItemKey();
@@ -151,7 +158,6 @@
 
     // layout configuration
 
-    bool autoWorkingArea;
     bool temporaryPlacement;
 
     qreal visibilityTolerance;
@@ -159,8 +165,6 @@
     // item manipulation functions
     void performTemporaryPlacement(int group, int itemInGroup);
     void revertTemporaryPlacement(int group, int itemInGroup);
-
-    bool m_activated;
 };
 
 #endif
Index: comic/comic.cpp
===================================================================
--- comic/comic.cpp	(.../trunk/KDE/kdeplasma-addons/applets)	(revision 922979)
+++ comic/comic.cpp	(.../branches/work/plasma-desktoplayout/kdeplasma-addons-applets)	(revision 922979)
@@ -569,6 +569,7 @@
         }
 
         resize( mLastSize );
+        emit appletTransformedItself();
     }
 }
 
Index: charselect/charselect.cpp
===================================================================
--- charselect/charselect.cpp	(.../trunk/KDE/kdeplasma-addons/applets)	(revision 922979)
+++ charselect/charselect.cpp	(.../branches/work/plasma-desktoplayout/kdeplasma-addons-applets)	(revision 922979)
@@ -46,6 +46,7 @@
         if (size().width() < widget()->size().width() || 
              size().height() < widget()->size().height()) {
             resize(widget()->size());
+            emit appletTransformedItself();
         }
     }
 }
Index: fuzzy-clock/fuzzyClock.cpp
===================================================================
--- fuzzy-clock/fuzzyClock.cpp	(.../trunk/KDE/kdeplasma-addons/applets)	(revision 922979)
+++ fuzzy-clock/fuzzyClock.cpp	(.../branches/work/plasma-desktoplayout/kdeplasma-addons-applets)	(revision 922979)
@@ -534,6 +534,7 @@
     } else {
         //add margins
         resize ( m_contentSize + QSizeF(size()-contentsRect().size()) );
+        emit appletTransformedItself();
     }
 
 } else { //in a panel or timestring wider than plasmoid -> change size to the minimal needed space, i.e. the timestring will not increase in point-size OR plasmoid in Panel.
@@ -667,6 +668,7 @@
         //we use the minimal height here, since the user has given us too much height we cannot use for anything useful. minimal width because we are in a panel.
         kDebug() << "we set the minimum size needed as the size we want";
         resize ( QSizeF ( m_minimumContentSize.width() + m_margin*2,m_minimumContentSize.height() ) + (size() - contentsRect().size()) );
+        emit appletTransformedItself();
     }
 }
 }
Index: frame/frame.cpp
===================================================================
--- frame/frame.cpp	(.../trunk/KDE/kdeplasma-addons/applets)	(revision 922979)
+++ frame/frame.cpp	(.../branches/work/plasma-desktoplayout/kdeplasma-addons-applets)	(revision 922979)
@@ -85,6 +85,7 @@
 	QSizeF sizeHint = contentSizeHint();
 	if (geometry().size() != sizeHint) {
             resize(sizeHint); 
+            emit appletTransformedItself();
 	} else {        
             update();
 	}
Index: binary-clock/binaryclock.cpp
===================================================================
--- binary-clock/binaryclock.cpp	(.../trunk/KDE/kdeplasma-addons/applets)	(revision 922979)
+++ binary-clock/binaryclock.cpp	(.../branches/work/plasma-desktoplayout/kdeplasma-addons-applets)	(revision 922979)
@@ -97,6 +97,7 @@
 
         } else {
             resize(getWidthFromHeight((int) contentsRect().height()) + borderWidth, contentsRect().height() + borderHeight);
+            emit appletTransformedItself();
         }
     }
 }
_______________________________________________
Plasma-devel mailing list
Plasma-devel@kde.org
https://mail.kde.org/mailman/listinfo/plasma-devel

Reply via email to