2 Copyright (c) 2008 Ambroz Bizjak <ambro@b4ever.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
12 #include <QCoreApplication>
13 #include <QGraphicsWidget>
14 #include <QGraphicsProxyWidget>
15 #include <QWaitCondition>
19 #include <Plasma/Animator>
21 #include "desktoplayout.h"
23 DesktopLayout::DesktopLayout(QGraphicsLayoutItem
*parent
)
25 QGraphicsLayout(parent
),
26 autoWorkingArea(true),
27 temporaryPlacement(false),
28 visibilityTolerance(0),
31 connect(Plasma::Animator::self(), SIGNAL(movementFinished(QGraphicsItem
*)),
32 this, SLOT(movementFinished(QGraphicsItem
*)));
35 void DesktopLayout::addItem(QGraphicsLayoutItem
*item
, bool pushBack
, const QRectF
&preferredGeom
, const QRectF
&lastGeom
)
37 int key
= newItemKey();
39 ItemSpace::ItemSpaceItem spaceItem
;
40 spaceItem
.pushBack
= pushBack
;
41 spaceItem
.animateMovement
= false;
42 spaceItem
.preferredGeometry
= preferredGeom
;
43 spaceItem
.lastGeometry
= (lastGeom
.isValid() ? lastGeom
: preferredGeom
);
44 spaceItem
.user
= QVariant(key
);
46 DesktopLayoutItem desktopItem
;
47 desktopItem
.item
= item
;
48 desktopItem
.temporaryGeometry
= QRectF(0, 0, -1, -1);
50 itemSpace
.addItem(spaceItem
);
51 items
.insert(key
, desktopItem
);
56 void DesktopLayout::addItem(QGraphicsLayoutItem
*item
, bool pushBack
, const QSizeF
&size
)
58 QSizeF itemSize
= ( size
.isValid() ? size
: item
->effectiveSizeHint(Qt::PreferredSize
) );
60 // get possible positions
61 QList
<QPointF
> possiblePositions
= itemSpace
.positionVertically(itemSize
, itemSpace
.spaceAlignment
, false, true);
62 //kDebug() << "possiblePositions" << possiblePositions;
64 // prefer free positions
65 QRectF bestGeometry
= QRectF();
66 foreach (const QPointF
&position
, possiblePositions
) {
67 QRectF geom
= QRectF(position
, itemSize
);
68 if (itemSpace
.positionedProperly(geom
)) {
74 if (!bestGeometry
.isValid()) {
75 // choose the position that with the best resulting visibility
76 QPointF bestPosition
= QPointF();
77 qreal bestVisibility
= 0;
78 foreach (const QPointF
&position
, possiblePositions
) {
79 // see how much the item can be pushed into the working area:
80 // copy our ItemSpace, add the item to the copy, activate it
81 // and check the resulting position's visibility
83 ItemSpace
tempItemSpace(itemSpace
);
85 ItemSpace::ItemSpaceItem spaceItem
;
86 spaceItem
.pushBack
= pushBack
;
87 spaceItem
.animateMovement
= false;
88 spaceItem
.preferredGeometry
= QRectF(position
, itemSize
);
89 spaceItem
.lastGeometry
= QRectF(position
, itemSize
);
90 spaceItem
.user
= QVariant(-1);
92 tempItemSpace
.addItem(spaceItem
);
93 tempItemSpace
.activate();
94 int tempGroup
, tempItem
;
95 tempItemSpace
.locateItemByUser(QVariant(-1), &tempGroup
, &tempItem
);
97 QRectF resultingGeom
= tempItemSpace
.m_groups
[tempGroup
].m_groupItems
[tempItem
].lastGeometry
;
98 qreal visibility
= tempItemSpace
.positionVisibility(resultingGeom
);
100 //kDebug() << "Trying " << position << " visibility " << visibility;
102 if (visibility
> bestVisibility
) {
103 bestPosition
= position
;
104 bestVisibility
= visibility
;
105 if (visibility
>= 1) {
111 if (bestVisibility
< (1.0-visibilityTolerance
)) {
112 bestPosition
= QPointF(itemSpace
.screenSpacing
, itemSpace
.screenSpacing
);
115 bestGeometry
= QRectF(bestPosition
, itemSize
);
118 addItem(item
, pushBack
, bestGeometry
);
119 kDebug() << "Positioned item to" << bestGeometry
;
122 bool DesktopLayout::getPushBack(int index
)
126 itemSpace
.locateItemByPosition(index
, &group
, &item
);
128 return itemSpace
.m_groups
[group
].m_groupItems
[item
].pushBack
;
131 QRectF
DesktopLayout::getPreferredGeometry(int index
)
135 itemSpace
.locateItemByPosition(index
, &group
, &item
);
137 return itemSpace
.m_groups
[group
].m_groupItems
[item
].preferredGeometry
;
140 QRectF
DesktopLayout::getLastGeometry(int index
)
144 itemSpace
.locateItemByPosition(index
, &group
, &item
);
146 return itemSpace
.m_groups
[group
].m_groupItems
[item
].lastGeometry
;
149 void DesktopLayout::setPlacementSpacing(qreal spacing
)
151 itemSpace
.placementSpacing
= spacing
;
154 void DesktopLayout::setScreenSpacing(qreal spacing
)
156 itemSpace
.screenSpacing
= spacing
;
160 void DesktopLayout::setShiftingSpacing(qreal spacing
)
162 itemSpace
.shiftingSpacing
= spacing
;
163 // NOTE: not wise to call that during operation yet
166 void DesktopLayout::setVisibilityTolerance(qreal part
)
168 visibilityTolerance
= part
;
172 void DesktopLayout::setWorkingArea(QRectF area
)
174 // itemSpace positions are relative to working area start,
175 // adjust them to correspond to the same on-screen positions
176 itemSpace
.offsetPositions(workingStart
- area
.topLeft());
177 itemSpace
.setWorkingArea(area
.size());
178 workingStart
= area
.topLeft();
182 void DesktopLayout::setAlignment(Qt::Alignment alignment
)
184 itemSpace
.spaceAlignment
= alignment
;
188 void DesktopLayout::setTemporaryPlacement(bool enabled
)
190 temporaryPlacement
= enabled
;
194 void DesktopLayout::setAutoWorkingArea (bool value
)
196 autoWorkingArea
= value
;
199 int DesktopLayout::count () const
204 QGraphicsLayoutItem
*DesktopLayout::itemAt (int i
) const
206 int group
= -2, item
= -2;
207 itemSpace
.locateItemByPosition(i
, &group
, &item
);
208 int itemKey
= itemSpace
.m_groups
[group
].m_groupItems
[item
].user
.toInt();
210 return items
[itemKey
].item
;
213 void DesktopLayout::removeAt (int i
)
216 itemSpace
.locateItemByPosition(i
, &group
, &item
);
217 int itemKey
= itemSpace
.m_groups
[group
].m_groupItems
[item
].user
.toInt();
219 // remove from ItemSpace
220 itemSpace
.removeItem(group
, item
);
221 // remove from local list
222 items
.remove(itemKey
);
227 void DesktopLayout::performTemporaryPlacement(int group
, int itemInGroup
)
229 ItemSpace::ItemSpaceItem
&spaceItem
= itemSpace
.m_groups
[group
].m_groupItems
[itemInGroup
];
230 DesktopLayoutItem
&item
= items
[spaceItem
.user
.toInt()];
232 QRectF origGeom
= spaceItem
.lastGeometry
;
233 spaceItem
.lastGeometry
= QRectF();
235 QPointF newPos
= QPointF(0, 0);
236 QList
<QPointF
> possiblePositions
= itemSpace
.positionVertically(origGeom
.size(), itemSpace
.spaceAlignment
, true, false);
237 if (possiblePositions
.count() > 0) {
238 newPos
= possiblePositions
[0];
241 spaceItem
.lastGeometry
= origGeom
;
242 item
.temporaryGeometry
= QRectF(newPos
, origGeom
.size());
243 item
.item
->setGeometry(item
.temporaryGeometry
.translated(workingStart
));
246 void DesktopLayout::revertTemporaryPlacement(int group
, int itemInGroup
)
248 ItemSpace::ItemSpaceItem
&spaceItem
= itemSpace
.m_groups
[group
].m_groupItems
[itemInGroup
];
249 DesktopLayoutItem
&item
= items
[spaceItem
.user
.toInt()];
251 item
.temporaryGeometry
= QRectF();
252 item
.item
->setGeometry(spaceItem
.lastGeometry
.translated(workingStart
));
255 // update anything that needs updating
256 void DesktopLayout::setGeometry(const QRectF
&rect
)
259 QGraphicsLayout::setGeometry(rect
);
261 if (autoWorkingArea
|| !itemSpace
.workingGeom
.isValid()) {
262 setWorkingArea(rect
);
265 // activate the ItemSpace to perform motion as needed
266 itemSpace
.activate();
268 for (int groupId
= 0; groupId
< itemSpace
.m_groups
.size(); groupId
++) {
269 ItemSpace::ItemGroup
&group
= itemSpace
.m_groups
[groupId
];
271 for (int itemId
= 0; itemId
< group
.m_groupItems
.size(); itemId
++) {
272 ItemSpace::ItemSpaceItem
&spaceItem
= group
.m_groupItems
[itemId
];
273 DesktopLayoutItem
&desktopItem
= items
[spaceItem
.user
.toInt()];
275 // Temporarily place the item if it could not be pushed inside the working area.
276 // Put it back if it fits again.
277 if (itemSpace
.positionVisibility(spaceItem
.lastGeometry
) < visibilityTolerance
) {
278 performTemporaryPlacement(groupId
, itemId
);
279 } else if (desktopItem
.temporaryGeometry
.isValid()) {
280 revertTemporaryPlacement(groupId
, itemId
);
283 // Reset the absolute position if needed
284 QRectF visibleGeom
= (desktopItem
.temporaryGeometry
.isValid() ? desktopItem
.temporaryGeometry
: spaceItem
.lastGeometry
);
285 QRectF absoluteGeom
= visibleGeom
.translated(workingStart
);
286 if (desktopItem
.item
->geometry() != absoluteGeom
) {
287 QGraphicsWidget
*w
= dynamic_cast<QGraphicsWidget
*>(desktopItem
.item
);
288 if (w
&& spaceItem
.animateMovement
) {
289 Plasma::Animator
*anim
= Plasma::Animator::self();
290 bool animating
= m_animatingItems
.contains(w
);
292 anim
->stopItemMovement(m_animatingItems
.value(w
));
294 int id
= Plasma::Animator::self()->moveItem(w
, Plasma::Animator::FastSlideInMovement
,
295 absoluteGeom
.topLeft().toPoint());
297 m_animatingItems
.insert(w
, id
);
298 } else if (animating
) {
299 m_animatingItems
.remove(w
);
302 spaceItem
.animateMovement
= false;
304 desktopItem
.item
->setGeometry(absoluteGeom
);
312 // This should be called when the geometry of an item has been changed.
313 // If the change was made by the user, the new position is used as the preferred position.
314 void DesktopLayout::itemGeometryChanged(QGraphicsLayoutItem
*layoutItem
)
316 if (m_activated
|| m_animatingItems
.contains(dynamic_cast<QGraphicsWidget
*>(layoutItem
))) {
320 // get local item key
322 QMapIterator
<int, DesktopLayoutItem
> i(items
);
323 while (i
.hasNext()) {
325 if (i
.value().item
== layoutItem
) {
336 itemSpace
.locateItemByUser(itemKey
, &group
, &item
);
337 ItemSpace::ItemSpaceItem
&spaceItem
= itemSpace
.m_groups
[group
].m_groupItems
[item
];
339 QRectF currentRelative
= layoutItem
->geometry().translated(-workingStart
);
340 if (spaceItem
.lastGeometry
!= currentRelative
) {
341 spaceItem
.lastGeometry
= currentRelative
;
342 spaceItem
.preferredGeometry
= currentRelative
;
344 itemSpace
.updateItem(group
, item
);
349 QSizeF
DesktopLayout::sizeHint (Qt::SizeHint which
, const QSizeF
&constraint
) const
356 void DesktopLayout::movementFinished(QGraphicsItem
* item
)
358 if (m_animatingItems
.contains(item
)) {
359 m_animatingItems
.remove(item
);
363 int DesktopLayout::newItemKey()
366 QList
<int> usedKeys
= items
.keys();
367 foreach (int key
, usedKeys
) {
368 if (key
- from
> 1) {
376 #include <desktoplayout.moc>