kde-workspace/plasma/containments/desktop/itemspace.cpp
Ivailo Monev 08bd97aff5 generic: compiler warning fixes
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-06-13 17:59:56 +03:00

915 lines
33 KiB
C++

/*
Copyright (c) 2008 Ambroz Bizjak <ambro@b4ever.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include <limits>
#include <KDebug>
#include "itemspace.h"
// Contrary to QRectF::intersects(), this function requires the
// rectangles to truly intersect, not just touch on one side.
// Further, we require a minimal overlap distance to prevent
// an infinite loop in the positioning algorithm due to some
// precision and optimization problems.
bool __intersects (const QRectF &a, const QRectF &b)
{
if (!(a.bottom() - 0.001 > b.top())) return false;
if (!(a.top() + 0.001 < b.bottom())) return false;
if (!(a.right() - 0.001 > b.left())) return false;
if (!(a.left() + 0.001 < b.right())) return false;
return true;
}
ItemSpace::ItemSpace()
: spaceAlignment(Qt::AlignTop|Qt::AlignLeft),
workingGeom(QSizeF()),
placementSpacing(0),
screenSpacing(0),
shiftingSpacing(0)
{
}
void ItemSpace::setWorkingArea(const QSizeF& area)
{
if (workingGeom.isValid()) {
// if the working area size changed and alignment includes right or bottom,
// the difference is added to all positions to keep items in the same place
// relative to the borders of alignment
if (((spaceAlignment & Qt::AlignRight) || (spaceAlignment & Qt::AlignBottom)) &&
(area.width() != workingGeom.width() || area.height() != workingGeom.height())) {
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::checkBorders()
{
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 intersecting working area borders inside.
For borders adjunct to the corner of alignment, allow pushing
over the opposite border (and items there may be temporarily placed).
*/
// left border
push = screenSpacing - item.lastGeometry.left();
if (push > 0) {
item.animateMovement = true;
power = PushAwayFromPreferred;
if ((spaceAlignment & Qt::AlignLeft)) {
power |= PushOverBorder;
}
performPush(groupId, DirRight, push, power);
}
// right border
push = item.lastGeometry.right()+screenSpacing - workingGeom.width();
if (push > 0) {
item.animateMovement = true;
power = PushAwayFromPreferred;
if ((spaceAlignment & Qt::AlignRight)) {
power |= PushOverBorder;
}
performPush(groupId, DirLeft, push, power);
}
// top border
push = screenSpacing - item.lastGeometry.top();
if (push > 0) {
item.animateMovement = true;
power = PushAwayFromPreferred;
if ((spaceAlignment & Qt::AlignTop)) {
power |= PushOverBorder;
}
performPush(groupId, DirDown, push, power);
}
// bottom border
push = item.lastGeometry.bottom()+screenSpacing - workingGeom.height();
if (push > 0) {
item.animateMovement = true;
power = PushAwayFromPreferred;
if ((spaceAlignment & Qt::AlignBottom)) {
power |= PushOverBorder;
}
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;
/*
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 = 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 = preferredGeometry.top() - item.lastGeometry.top();
if (push > 0) {
performPush(groupId, DirDown, push, NoPower);
} else if (push < 0) {
performPush(groupId, DirUp, -push, NoPower);
}
}
}
}
}
qreal ItemSpace::positionVisibility (const QRectF& geom)
{
QRectF visibleArea = QRectF(QPointF(), workingGeom);
QRectF visibleItemPart = visibleArea.intersected(geom);
qreal itemSurface = geom.width() * geom.height();
qreal itemVisibleSurface = visibleItemPart.width() * visibleItemPart.height();
return (itemVisibleSurface / itemSurface);
}
void ItemSpace::offsetPositions(const QPointF &offset)
{
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];
item.preferredPosition += offset;
item.lastGeometry.adjust(offset.x(), offset.y(), offset.x(), offset.y());
}
}
}
qreal ItemSpace::performPush(int groupId, Direction direction, qreal amount, PushPower power)
{
ItemGroup &group = m_groups[groupId];
preparePush(direction, power);
group.addRequest(this, ItemGroup::Request(-1, 0, amount));
group.applyResults(this, -1);
return group.m_pushAvailable;
}
bool ItemSpace::positionedProperly(const QRectF& itemGeom)
{
QRectF fullGeom = itemGeom.adjusted(-placementSpacing, -placementSpacing, placementSpacing, placementSpacing);
return (QRectF(QPointF(), workingGeom).contains(fullGeom));
}
QList<QPointF> ItemSpace::positionVertically(
const QSizeF &itemSize,
Qt::Alignment align,
bool limitedSpace,
bool findAll
) const
{
qreal spL = placementSpacing;
qreal spR = placementSpacing;
qreal spT = placementSpacing;
qreal spB = placementSpacing;
QList<QPointF> possiblePositions;
// basically, position searching is done by repetedly looking for obstacles at
// one position and looking for a next position to try
// add spacing to the size
QSizeF size = QSizeF(itemSize.width()+spL+spR, itemSize.height()+spT+spB);
// the initial x coordinate to start testing
// start either on the left or the right, and advance inside
qreal x = ((align & Qt::AlignLeft) ? 0 : workingGeom.width()-size.width());
// try different x coordinates
while (1) {
// stop testing if we're limited by the working area and positions at the next x would reach outside
bool outOfX = ((align & Qt::AlignLeft) ? (x + size.width() > workingGeom.width()) : (x < 0));
if (outOfX && limitedSpace) {
break;
}
// the initial y coordinate to start testing heights at the current x
// start either on the top or the bottom, and advance inside
qreal y = ((align & Qt::AlignTop) ? 0 : workingGeom.height()-size.height());
// try different y coordinates
while (1) {
// stop testing at this x if we're limited by the working area and positions at the next y would reach outside
bool outOfY = ((align & Qt::AlignTop) ? (y + size.height() > workingGeom.height()) : (y < 0));
if (outOfY && limitedSpace) {
break;
}
// Z would come here :)
// Check for intersecting items, or a new y coordinate to try.
// Suppose we're aligning to top:
// Find all items that intersect the region we're testing.
// If no items were found, we have space.
// Oterwise pick the one with the lowest bottom border
// and use that border as the new y coordinate to try.
// The logic is inverted when aligning to bottom.
QRectF a;
if ((align & Qt::AlignTop)) {
a = itemInRegionEndingLastVert(QRectF(x, y, size.width(), size.height()));
} else {
a = itemInRegionStartingFirstVert(QRectF(x, y, size.width(), size.height()));
}
if (!a.isValid()) {
// found a valid position
possiblePositions.append(QPointF(x+spL, y+spT));
if (!findAll) {
return possiblePositions;
}
// don't look at this X anymore, one position is enough
break;
}
if ((align & Qt::AlignTop)) {
y = a.bottom();
} else {
y = a.y() - size.height();
}
}
// Find next possible x coordinate
// Suppose we're aligning to left:
// Take a vertical strap of the area we have been testing previously,
// extending over the height of the working area.
// Find all items that intersect the region we're testing.
// If no items were found, stop all testing.
// Otherwise, pick the one with the most-left right border
// and use that border as the new x coordinate to try.
// The logic is inverted when aligning to right.
QRectF a;
if ((align & Qt::AlignLeft)) {
a = itemInRegionEndingFirstHoriz(QRectF(x, 0, size.width(), workingGeom.height()));
} else {
a = itemInRegionStartingLastHoriz(QRectF(x, 0, size.width(), workingGeom.height()));
}
if (!a.isValid()) {
break;
}
if ((align & Qt::AlignLeft)) {
x = a.right();
} else {
x = a.x() - size.width();
}
}
return possiblePositions;
}
QRectF ItemSpace::itemInRegionStartingFirstVert(const QRectF &region) const
{
QRectF ret = QRectF(0,0,-1,-1);
qreal l = std::numeric_limits<qreal>::max();
for (int groupId = 0; groupId < m_groups.size(); groupId++) {
const ItemGroup &group = m_groups[groupId];
for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
const ItemSpaceItem &item = group.m_groupItems[itemId];
if (!item.lastGeometry.isValid()) {
continue;
}
qreal cl = item.lastGeometry.y();
if (__intersects(item.lastGeometry, region) && cl < l) {
ret = item.lastGeometry;
l = cl;
}
}
}
return ret;
}
QRectF ItemSpace::itemInRegionEndingLastVert(const QRectF &region) const
{
QRectF ret = QRectF(0,0,-1,-1);
qreal l = -1;
for (int groupId = 0; groupId < m_groups.size(); groupId++) {
const ItemGroup &group = m_groups[groupId];
for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
const ItemSpaceItem &item = group.m_groupItems[itemId];
if (!item.lastGeometry.isValid()) {
continue;
}
qreal cl = item.lastGeometry.y() + item.lastGeometry.height();
if (__intersects(item.lastGeometry, region) && cl > l) {
ret = item.lastGeometry;
l = cl;
}
}
}
return ret;
}
QRectF ItemSpace::itemInRegionEndingFirstHoriz(const QRectF &region) const
{
QRectF ret = QRectF(0,0,-1,-1);
qreal l = std::numeric_limits<qreal>::max();
for (int groupId = 0; groupId < m_groups.size(); groupId++) {
const ItemGroup &group = m_groups[groupId];
for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
const ItemSpaceItem &item = group.m_groupItems[itemId];
if (!item.lastGeometry.isValid()) {
continue;
}
qreal cl = item.lastGeometry.x() + item.lastGeometry.width();
if (__intersects(item.lastGeometry, region) && cl < l) {
ret = item.lastGeometry;
l = cl;
}
}
}
return ret;
}
QRectF ItemSpace::itemInRegionStartingLastHoriz(const QRectF &region) const
{
QRectF ret = QRectF(0,0,-1,-1);
qreal l = -1;
for (int groupId = 0; groupId < m_groups.size(); groupId++) {
const ItemGroup &group = m_groups[groupId];
for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
const ItemSpaceItem &item = group.m_groupItems[itemId];
if (!item.lastGeometry.isValid()) {
continue;
}
qreal cl = item.lastGeometry.x();
if (__intersects(item.lastGeometry, region) && cl > l) {
ret = item.lastGeometry;
l = cl;
}
}
}
return ret;
}
ItemSpace::ItemGroup::Request::Request(
int sourceGroup,
qreal sourceGroupPushRequested,
qreal pushRequested
)
: m_sourceGroup(sourceGroup),
m_sourceGroupPushRequested(sourceGroupPushRequested),
m_pushRequested(pushRequested),
m_compensated(false)
{
}
void ItemSpace::ItemGroup::Request::activate (ItemSpace *itemSpace, ItemGroup *group)
{
// don't do anything if the group was already asked to move at
// least as much as we ask
if (group->m_largestPushRequested >= m_pushRequested) {
return;
}
qreal largest = group->m_largestPushRequested;
// record our request as the largest
group->m_largestPushRequested = m_pushRequested;
// don't do anything if the group already hit an unmovable obstacle
if (group->m_pushAvailable < largest) {
return;
}
// set the available push to our requested value
// and limit it as obstacles are found
group->m_pushAvailable = m_pushRequested;
// look for obstacles for every item in the group
for (int itemId = 0; itemId < group->m_groupItems.size(); itemId++) {
ItemSpaceItem &item = group->m_groupItems[itemId];
QRectF origGeom = item.lastGeometry;
QRectF fullGeom = origGeom.adjusted(-itemSpace->shiftingSpacing, -itemSpace->shiftingSpacing,
itemSpace->shiftingSpacing, itemSpace->shiftingSpacing);
// limit push by screen boundaries
if (!(itemSpace->m_power & PushOverBorder)) {
qreal limit;
switch (itemSpace->m_direction) {
case DirLeft:
limit = origGeom.left() - itemSpace->screenSpacing;
break;
case DirRight:
limit = itemSpace->workingGeom.width() - itemSpace->screenSpacing - origGeom.right();
break;
case DirUp:
limit = origGeom.top() - itemSpace->screenSpacing;
break;
case DirDown:
limit = itemSpace->workingGeom.height() - itemSpace->screenSpacing - origGeom.bottom();
break;
}
group->m_pushAvailable = qMax(qreal(0.0), qMin(group->m_pushAvailable, limit));
if (group->m_pushAvailable == 0) {
break;
}
}
// 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() - preferredGeometry.left();
break;
case DirRight:
limit = -(origGeom.left() - preferredGeometry.left());
break;
case DirUp:
limit = origGeom.top() - preferredGeometry.top();
break;
case DirDown:
limit = -(origGeom.top() - preferredGeometry.top());
break;
}
limit = qMax(qreal(0.0), limit);
group->m_pushAvailable = qMin(group->m_pushAvailable, limit);
if (group->m_pushAvailable == 0) {
break;
}
}
// look for items in the way
for (int testGroupId = 0; testGroupId < itemSpace->m_groups.size(); testGroupId++) {
QList<int> asa;
if (testGroupId == group->m_id || group->groupIsAbove(itemSpace, asa, testGroupId)) {
continue;
}
ItemGroup &testGroup = itemSpace->m_groups[testGroupId];
// calculate how much the offending group needs to be pushed
qreal groupPush = 0;
for (int testItemId = 0; testItemId < testGroup.m_groupItems.size(); testItemId++) {
ItemSpaceItem &testItem = testGroup.m_groupItems[testItemId];
QRectF newlyTakenSpace;
qreal push;
switch (itemSpace->m_direction) {
case DirLeft:
newlyTakenSpace = QRectF(fullGeom.left() - group->m_pushAvailable, fullGeom.top(), group->m_pushAvailable, fullGeom.height());
push = testItem.lastGeometry.right() - newlyTakenSpace.left();
break;
case DirRight:
newlyTakenSpace = QRectF(fullGeom.right(), fullGeom.top(), group->m_pushAvailable, fullGeom.height());
push = newlyTakenSpace.right() - testItem.lastGeometry.left();
break;
case DirUp:
newlyTakenSpace = QRectF(fullGeom.left(), fullGeom.top() - group->m_pushAvailable, fullGeom.width(), group->m_pushAvailable);
push = testItem.lastGeometry.bottom() - newlyTakenSpace.top();
break;
case DirDown:
newlyTakenSpace = QRectF(fullGeom.left(), fullGeom.bottom(), fullGeom.width(), group->m_pushAvailable);
push = newlyTakenSpace.bottom() - testItem.lastGeometry.top();
break;
}
// check if it is an obstacle
if (testItem.lastGeometry.intersects(newlyTakenSpace)) {
groupPush = qMax(groupPush, push);
}
}
if (groupPush == 0) {
continue;
}
// post a move request to the obstacle
if (!group->m_obstacles.contains(testGroupId)) {
group->m_obstacles.append(testGroupId);
}
testGroup.addRequest(itemSpace, Request(group->m_id, group->m_pushAvailable, groupPush));
// limit our push by how much the obstacle can actually move
if (testGroup.m_pushAvailable < groupPush) {
group->m_pushAvailable = qMax(qreal(0.0), group->m_pushAvailable - (groupPush - testGroup.m_pushAvailable));
if (group->m_pushAvailable == 0) {
break;
}
}
}
}
}
void ItemSpace::ItemGroup::resetPush(int id)
{
m_id = id;
m_largestPushRequested = 0,
m_pushAvailable = std::numeric_limits<qreal>::max();
m_requests = QList<Request>();
m_obstacles = QList<int>();
}
void ItemSpace::ItemGroup::addRequest (ItemSpace *itemSpace, const class Request &request)
{
m_requests.append(request);
m_requests.last().activate(itemSpace, this);
}
void ItemSpace::ItemGroup::applyResults(ItemSpace *itemSpace, int cameFrom)
{
bool notComplete = false;
for (int i = 0; i < m_requests.size(); i++) {
Request &request = m_requests[i];
if (request.m_sourceGroup == -1) {
continue;
}
if (request.m_sourceGroup == cameFrom) {
qreal pushLost = request.m_sourceGroupPushRequested - itemSpace->m_groups[cameFrom].m_pushAvailable;
request.m_pushRequested -= pushLost;
request.m_compensated = true;
} else if (!request.m_compensated) {
notComplete = true;
}
}
if (notComplete) {
return;
}
qreal totalPushRequired = 0;
for (int i = 0; i < m_requests.size(); i++) {
Request &request = m_requests[i];
totalPushRequired = qMax(totalPushRequired, request.m_pushRequested);
}
m_pushAvailable = qMin(m_pushAvailable, totalPushRequired);
for (int groupId = 0; groupId < m_groupItems.size(); groupId++) {
ItemSpaceItem &groupItem = m_groupItems[groupId];
switch (itemSpace->m_direction) {
case DirLeft:
groupItem.lastGeometry = groupItem.lastGeometry.adjusted(-m_pushAvailable, 0, -m_pushAvailable, 0);
break;
case DirRight:
groupItem.lastGeometry = groupItem.lastGeometry.adjusted(m_pushAvailable, 0, m_pushAvailable, 0);
break;
case DirUp:
groupItem.lastGeometry = groupItem.lastGeometry.adjusted(0, -m_pushAvailable, 0, -m_pushAvailable);
break;
case DirDown:
groupItem.lastGeometry = groupItem.lastGeometry.adjusted(0, m_pushAvailable, 0, m_pushAvailable);
break;
}
}
foreach (int obstacleId, m_obstacles) {
itemSpace->m_groups[obstacleId].applyResults(itemSpace, m_id);
}
}
bool ItemSpace::ItemGroup::groupIsAbove(ItemSpace *itemSpace, QList<int> &visited, int groupId)
{
foreach (const Request &request, m_requests) {
if (request.m_sourceGroup == -1 || visited.contains(request.m_sourceGroup)) {
continue;
}
if (request.m_sourceGroup == groupId) {
return true;
}
visited.append(request.m_sourceGroup);
if (itemSpace->m_groups[request.m_sourceGroup].groupIsAbove(itemSpace, visited, groupId)) {
return true;
}
}
return false;
}
// TODO: optimize
void ItemSpace::linkItem(ItemSpaceItem newItem)
{
QList<ItemSpaceItem> newGroupItems;
QRectF newItemGeom = newItem.lastGeometry.adjusted(-shiftingSpacing, -shiftingSpacing,
shiftingSpacing, shiftingSpacing);
// look for items overlapping with the new item
for (int groupId = 0; groupId < m_groups.size();) {
ItemGroup &group = m_groups[groupId];
// if any item in the group overlaps it, save its items and remove the group
bool removeGroup = false;
for (int itemId = 0; itemId < group.m_groupItems.size(); itemId++) {
ItemSpaceItem &item = group.m_groupItems[itemId];
if (newItemGeom.intersects(item.lastGeometry)) {
removeGroup = true;
break;
}
}
if (removeGroup) {
newGroupItems << group.m_groupItems;
m_groups.removeAt(groupId);
} else {
groupId++;
}
}
// create the new group
m_groups.append(ItemGroup());
ItemGroup &newGroup = m_groups.last();
newGroup.m_groupItems.append(newItem);
newGroup.m_groupItems << newGroupItems;
}
// TODO: optimize
void ItemSpace::unlinkItem(int removeGroup, int removeItemInGroup)
{
// remove items from group
m_groups[removeGroup].m_groupItems.removeAt(removeItemInGroup);
// save other group items
QList<ItemSpaceItem> otherGroupItems = m_groups[removeGroup].m_groupItems;
// remove group
m_groups.removeAt(removeGroup);
// re-add other group items
foreach (const ItemSpaceItem &item, otherGroupItems) {
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::moveItem(int groupIndex, int itemInGroup, const QRectF& newGeom)
{
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, const 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;
for (int groupId = 0; groupId < m_groups.size(); groupId++) {
ItemGroup group = m_groups[groupId];
if (current + group.m_groupItems.size() > pos) {
*groupIndex = groupId;
*itemInGroup = pos - current;
return true;
}
current += group.m_groupItems.size();
}
return false;
}
bool ItemSpace::locateItemByUser(QVariant user, int *groupIndex, int *itemInGroup) const
{
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 (item.user == user) {
*groupIndex = groupId;
*itemInGroup = itemId;
return true;
}
}
}
return false;
}
void ItemSpace::preparePush(Direction direction, PushPower power)
{
m_direction = direction;
m_power = power;
for (int i=0; i<m_groups.size(); i++) {
m_groups[i].resetPush(i);
}
}