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 ®ion) const; QRectF itemInRegionEndingLastVert(const QRectF ®ion) const; QRectF itemInRegionEndingFirstHoriz(const QRectF ®ion) 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