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
[email protected]
https://mail.kde.org/mailman/listinfo/plasma-devel