File indexing completed on 2024-11-10 04:57:05
0001 /* 0002 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 // The layouting code is taken from the present windows effect. 0005 SPDX-FileCopyrightText: 2007 Rivo Laks <rivolaks@hot.ee> 0006 SPDX-FileCopyrightText: 2008 Lucas Murray <lmurray@undefinedfire.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "expolayout.h" 0012 0013 #include <cmath> 0014 0015 ExpoCell::ExpoCell(QObject *parent) 0016 : QObject(parent) 0017 { 0018 } 0019 0020 ExpoCell::~ExpoCell() 0021 { 0022 setLayout(nullptr); 0023 } 0024 0025 ExpoLayout *ExpoCell::layout() const 0026 { 0027 return m_layout; 0028 } 0029 0030 void ExpoCell::setLayout(ExpoLayout *layout) 0031 { 0032 if (m_layout == layout) { 0033 return; 0034 } 0035 if (m_layout) { 0036 m_layout->removeCell(this); 0037 } 0038 m_layout = layout; 0039 if (m_layout && m_enabled) { 0040 m_layout->addCell(this); 0041 } 0042 Q_EMIT layoutChanged(); 0043 } 0044 0045 bool ExpoCell::isEnabled() const 0046 { 0047 return m_enabled; 0048 } 0049 0050 void ExpoCell::setEnabled(bool enabled) 0051 { 0052 if (m_enabled != enabled) { 0053 m_enabled = enabled; 0054 if (enabled) { 0055 if (m_layout) { 0056 m_layout->addCell(this); 0057 } 0058 } else { 0059 if (m_layout) { 0060 m_layout->removeCell(this); 0061 } 0062 } 0063 Q_EMIT enabledChanged(); 0064 } 0065 } 0066 0067 void ExpoCell::update() 0068 { 0069 if (m_layout) { 0070 m_layout->polish(); 0071 } 0072 } 0073 0074 int ExpoCell::naturalX() const 0075 { 0076 return m_naturalX; 0077 } 0078 0079 void ExpoCell::setNaturalX(int x) 0080 { 0081 if (m_naturalX != x) { 0082 m_naturalX = x; 0083 update(); 0084 Q_EMIT naturalXChanged(); 0085 } 0086 } 0087 0088 int ExpoCell::naturalY() const 0089 { 0090 return m_naturalY; 0091 } 0092 0093 void ExpoCell::setNaturalY(int y) 0094 { 0095 if (m_naturalY != y) { 0096 m_naturalY = y; 0097 update(); 0098 Q_EMIT naturalYChanged(); 0099 } 0100 } 0101 0102 int ExpoCell::naturalWidth() const 0103 { 0104 return m_naturalWidth; 0105 } 0106 0107 void ExpoCell::setNaturalWidth(int width) 0108 { 0109 if (m_naturalWidth != width) { 0110 m_naturalWidth = width; 0111 update(); 0112 Q_EMIT naturalWidthChanged(); 0113 } 0114 } 0115 0116 int ExpoCell::naturalHeight() const 0117 { 0118 return m_naturalHeight; 0119 } 0120 0121 void ExpoCell::setNaturalHeight(int height) 0122 { 0123 if (m_naturalHeight != height) { 0124 m_naturalHeight = height; 0125 update(); 0126 Q_EMIT naturalHeightChanged(); 0127 } 0128 } 0129 0130 QRect ExpoCell::naturalRect() const 0131 { 0132 return QRect(naturalX(), naturalY(), naturalWidth(), naturalHeight()); 0133 } 0134 0135 QMargins ExpoCell::margins() const 0136 { 0137 return m_margins; 0138 } 0139 0140 int ExpoCell::x() const 0141 { 0142 return m_x.value_or(0); 0143 } 0144 0145 void ExpoCell::setX(int x) 0146 { 0147 if (m_x != x) { 0148 m_x = x; 0149 Q_EMIT xChanged(); 0150 } 0151 } 0152 0153 int ExpoCell::y() const 0154 { 0155 return m_y.value_or(0); 0156 } 0157 0158 void ExpoCell::setY(int y) 0159 { 0160 if (m_y != y) { 0161 m_y = y; 0162 Q_EMIT yChanged(); 0163 } 0164 } 0165 0166 int ExpoCell::width() const 0167 { 0168 return m_width.value_or(0); 0169 } 0170 0171 void ExpoCell::setWidth(int width) 0172 { 0173 if (m_width != width) { 0174 m_width = width; 0175 Q_EMIT widthChanged(); 0176 } 0177 } 0178 0179 int ExpoCell::height() const 0180 { 0181 return m_height.value_or(0); 0182 } 0183 0184 void ExpoCell::setHeight(int height) 0185 { 0186 if (m_height != height) { 0187 m_height = height; 0188 Q_EMIT heightChanged(); 0189 } 0190 } 0191 0192 QString ExpoCell::persistentKey() const 0193 { 0194 return m_persistentKey; 0195 } 0196 0197 void ExpoCell::setPersistentKey(const QString &key) 0198 { 0199 if (m_persistentKey != key) { 0200 m_persistentKey = key; 0201 update(); 0202 Q_EMIT persistentKeyChanged(); 0203 } 0204 } 0205 0206 int ExpoCell::bottomMargin() const 0207 { 0208 return m_margins.bottom(); 0209 } 0210 0211 void ExpoCell::setBottomMargin(int margin) 0212 { 0213 if (m_margins.bottom() != margin) { 0214 m_margins.setBottom(margin); 0215 update(); 0216 Q_EMIT bottomMarginChanged(); 0217 } 0218 } 0219 0220 ExpoLayout::ExpoLayout(QQuickItem *parent) 0221 : QQuickItem(parent) 0222 { 0223 } 0224 0225 ExpoLayout::LayoutMode ExpoLayout::mode() const 0226 { 0227 return m_mode; 0228 } 0229 0230 void ExpoLayout::setMode(LayoutMode mode) 0231 { 0232 if (m_mode != mode) { 0233 m_mode = mode; 0234 polish(); 0235 Q_EMIT modeChanged(); 0236 } 0237 } 0238 0239 bool ExpoLayout::fillGaps() const 0240 { 0241 return m_fillGaps; 0242 } 0243 0244 void ExpoLayout::setFillGaps(bool fill) 0245 { 0246 if (m_fillGaps != fill) { 0247 m_fillGaps = fill; 0248 polish(); 0249 Q_EMIT fillGapsChanged(); 0250 } 0251 } 0252 0253 int ExpoLayout::spacing() const 0254 { 0255 return m_spacing; 0256 } 0257 0258 void ExpoLayout::setSpacing(int spacing) 0259 { 0260 if (m_spacing != spacing) { 0261 m_spacing = spacing; 0262 polish(); 0263 Q_EMIT spacingChanged(); 0264 } 0265 } 0266 0267 bool ExpoLayout::isReady() const 0268 { 0269 return m_ready; 0270 } 0271 0272 void ExpoLayout::setReady() 0273 { 0274 if (!m_ready) { 0275 m_ready = true; 0276 Q_EMIT readyChanged(); 0277 } 0278 } 0279 0280 void ExpoLayout::forceLayout() 0281 { 0282 updatePolish(); 0283 } 0284 0285 void ExpoLayout::updatePolish() 0286 { 0287 if (!m_cells.isEmpty()) { 0288 switch (m_mode) { 0289 case LayoutClosest: 0290 calculateWindowTransformationsClosest(); 0291 break; 0292 case LayoutNatural: 0293 calculateWindowTransformationsNatural(); 0294 break; 0295 case LayoutNone: 0296 resetTransformations(); 0297 break; 0298 } 0299 } 0300 0301 setReady(); 0302 } 0303 0304 void ExpoLayout::addCell(ExpoCell *cell) 0305 { 0306 Q_ASSERT(!m_cells.contains(cell)); 0307 m_cells.append(cell); 0308 polish(); 0309 } 0310 0311 void ExpoLayout::removeCell(ExpoCell *cell) 0312 { 0313 m_cells.removeOne(cell); 0314 polish(); 0315 } 0316 0317 void ExpoLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) 0318 { 0319 if (newGeometry.size() != oldGeometry.size()) { 0320 polish(); 0321 } 0322 QQuickItem::geometryChange(newGeometry, oldGeometry); 0323 } 0324 0325 static int distance(const QPoint &a, const QPoint &b) 0326 { 0327 const int xdiff = a.x() - b.x(); 0328 const int ydiff = a.y() - b.y(); 0329 return int(std::sqrt(qreal(xdiff * xdiff + ydiff * ydiff))); 0330 } 0331 0332 static QRect centered(ExpoCell *cell, const QRect &bounds) 0333 { 0334 const QSize scaled = QSize(cell->naturalWidth(), cell->naturalHeight()) 0335 .scaled(bounds.size(), Qt::KeepAspectRatio); 0336 0337 return QRect(bounds.center().x() - scaled.width() / 2, 0338 bounds.center().y() - scaled.height() / 2, 0339 scaled.width(), 0340 scaled.height()); 0341 } 0342 0343 void ExpoLayout::calculateWindowTransformationsClosest() 0344 { 0345 QRect area = QRect(0, 0, width(), height()); 0346 const int columns = int(std::ceil(std::sqrt(qreal(m_cells.count())))); 0347 const int rows = int(std::ceil(m_cells.count() / qreal(columns))); 0348 0349 // Assign slots 0350 const int slotWidth = area.width() / columns; 0351 const int slotHeight = area.height() / rows; 0352 QList<ExpoCell *> takenSlots; 0353 takenSlots.resize(rows * columns); 0354 takenSlots.fill(nullptr); 0355 0356 // precalculate all slot centers 0357 QList<QPoint> slotCenters; 0358 slotCenters.resize(rows * columns); 0359 for (int x = 0; x < columns; ++x) { 0360 for (int y = 0; y < rows; ++y) { 0361 slotCenters[x + y * columns] = QPoint(area.x() + slotWidth * x + slotWidth / 2, 0362 area.y() + slotHeight * y + slotHeight / 2); 0363 } 0364 } 0365 0366 // Assign each window to the closest available slot 0367 QList<ExpoCell *> tmpList = m_cells; // use a QLinkedList copy instead? 0368 while (!tmpList.isEmpty()) { 0369 ExpoCell *cell = tmpList.first(); 0370 int slotCandidate = -1, slotCandidateDistance = INT_MAX; 0371 const QPoint pos = cell->naturalRect().center(); 0372 0373 for (int i = 0; i < columns * rows; ++i) { // all slots 0374 const int dist = distance(pos, slotCenters[i]); 0375 if (dist < slotCandidateDistance) { // window is interested in this slot 0376 ExpoCell *occupier = takenSlots[i]; 0377 Q_ASSERT(occupier != cell); 0378 if (!occupier || dist < distance(occupier->naturalRect().center(), slotCenters[i])) { 0379 // either nobody lives here, or we're better - takeover the slot if it's our best 0380 slotCandidate = i; 0381 slotCandidateDistance = dist; 0382 } 0383 } 0384 } 0385 Q_ASSERT(slotCandidate != -1); 0386 if (takenSlots[slotCandidate]) { 0387 tmpList << takenSlots[slotCandidate]; // occupier needs a new home now :p 0388 } 0389 tmpList.removeAll(cell); 0390 takenSlots[slotCandidate] = cell; // ...and we rumble in =) 0391 } 0392 0393 for (int slot = 0; slot < columns * rows; ++slot) { 0394 ExpoCell *cell = takenSlots[slot]; 0395 if (!cell) { // some slots might be empty 0396 continue; 0397 } 0398 0399 // Work out where the slot is 0400 QRect target(area.x() + (slot % columns) * slotWidth, 0401 area.y() + (slot / columns) * slotHeight, 0402 slotWidth, slotHeight); 0403 QRect adjustedTarget = target.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing); 0404 if (adjustedTarget.isValid()) { 0405 target = adjustedTarget; // Borders 0406 } 0407 target = target.marginsRemoved(cell->margins()); 0408 0409 qreal scale; 0410 if (target.width() / qreal(cell->naturalWidth()) < target.height() / qreal(cell->naturalHeight())) { 0411 // Center vertically 0412 scale = target.width() / qreal(cell->naturalWidth()); 0413 target.moveTop(target.top() + (target.height() - int(cell->naturalHeight() * scale)) / 2); 0414 target.setHeight(int(cell->naturalHeight() * scale)); 0415 } else { 0416 // Center horizontally 0417 scale = target.height() / qreal(cell->naturalHeight()); 0418 target.moveLeft(target.left() + (target.width() - int(cell->naturalWidth() * scale)) / 2); 0419 target.setWidth(int(cell->naturalWidth() * scale)); 0420 } 0421 // Don't scale the windows too much 0422 if (scale > 2.0 || (scale > 1.0 && (cell->naturalWidth() > 300 || cell->naturalHeight() > 300))) { 0423 scale = (cell->naturalWidth() > 300 || cell->naturalHeight() > 300) ? 1.0 : 2.0; 0424 target = QRect( 0425 target.center().x() - int(cell->naturalWidth() * scale) / 2, 0426 target.center().y() - int(cell->naturalHeight() * scale) / 2, 0427 scale * cell->naturalWidth(), scale * cell->naturalHeight()); 0428 } 0429 0430 cell->setX(target.x()); 0431 cell->setY(target.y()); 0432 cell->setWidth(target.width()); 0433 cell->setHeight(target.height()); 0434 } 0435 } 0436 0437 static inline int heightForWidth(ExpoCell *cell, int width) 0438 { 0439 return int((width / qreal(cell->naturalWidth())) * cell->naturalHeight()); 0440 } 0441 0442 static bool isOverlappingAny(ExpoCell *w, const QHash<ExpoCell *, QRect> &targets, const QRegion &border, int spacing) 0443 { 0444 QHash<ExpoCell *, QRect>::const_iterator winTarget = targets.find(w); 0445 if (winTarget == targets.constEnd()) { 0446 return false; 0447 } 0448 if (border.intersects(*winTarget)) { 0449 return true; 0450 } 0451 const QMargins halfSpacing(spacing / 2, spacing / 2, spacing / 2, spacing / 2); 0452 0453 // Is there a better way to do this? 0454 QHash<ExpoCell *, QRect>::const_iterator target; 0455 for (target = targets.constBegin(); target != targets.constEnd(); ++target) { 0456 if (target == winTarget) { 0457 continue; 0458 } 0459 if (winTarget->marginsAdded(halfSpacing).intersects(target->marginsAdded(halfSpacing))) { 0460 return true; 0461 } 0462 } 0463 return false; 0464 } 0465 0466 void ExpoLayout::calculateWindowTransformationsNatural() 0467 { 0468 const QRect area = QRect(0, 0, width(), height()); 0469 0470 // As we are using pseudo-random movement (See "slot") we need to make sure the list 0471 // is always sorted the same way no matter which window is currently active. 0472 std::sort(m_cells.begin(), m_cells.end(), [](const ExpoCell *a, const ExpoCell *b) { 0473 return a->persistentKey() < b->persistentKey(); 0474 }); 0475 0476 QRect bounds; 0477 int direction = 0; 0478 QHash<ExpoCell *, QRect> targets; 0479 QHash<ExpoCell *, int> directions; 0480 0481 for (ExpoCell *cell : std::as_const(m_cells)) { 0482 const QRect cellRect(cell->naturalX(), cell->naturalY(), cell->naturalWidth(), cell->naturalHeight()); 0483 targets[cell] = cellRect; 0484 // Reuse the unused "slot" as a preferred direction attribute. This is used when the window 0485 // is on the edge of the screen to try to use as much screen real estate as possible. 0486 directions[cell] = direction; 0487 bounds = bounds.united(cellRect); 0488 direction++; 0489 if (direction == 4) { 0490 direction = 0; 0491 } 0492 } 0493 0494 // Iterate over all windows, if two overlap push them apart _slightly_ as we try to 0495 // brute-force the most optimal positions over many iterations. 0496 const int halfSpacing = m_spacing / 2; 0497 bool overlap; 0498 do { 0499 overlap = false; 0500 for (ExpoCell *cell : std::as_const(m_cells)) { 0501 QRect *target_w = &targets[cell]; 0502 for (ExpoCell *e : std::as_const(m_cells)) { 0503 if (cell == e) { 0504 continue; 0505 } 0506 0507 QRect *target_e = &targets[e]; 0508 if (target_w->adjusted(-halfSpacing, -halfSpacing, halfSpacing, halfSpacing) 0509 .intersects(target_e->adjusted(-halfSpacing, -halfSpacing, halfSpacing, halfSpacing))) { 0510 overlap = true; 0511 0512 // Determine pushing direction 0513 QPoint diff(target_e->center() - target_w->center()); 0514 // Prevent dividing by zero and non-movement 0515 if (diff.x() == 0 && diff.y() == 0) { 0516 diff.setX(1); 0517 } 0518 // Try to keep screen aspect ratio 0519 // if (bounds.height() / bounds.width() > area.height() / area.width()) 0520 // diff.setY(diff.y() / 2); 0521 // else 0522 // diff.setX(diff.x() / 2); 0523 // Approximate a vector of between 10px and 20px in magnitude in the same direction 0524 diff *= m_accuracy / qreal(diff.manhattanLength()); 0525 // Move both windows apart 0526 target_w->translate(-diff); 0527 target_e->translate(diff); 0528 0529 // Try to keep the bounding rect the same aspect as the screen so that more 0530 // screen real estate is utilised. We do this by splitting the screen into nine 0531 // equal sections, if the window center is in any of the corner sections pull the 0532 // window towards the outer corner. If it is in any of the other edge sections 0533 // alternate between each corner on that edge. We don't want to determine it 0534 // randomly as it will not produce consistant locations when using the filter. 0535 // Only move one window so we don't cause large amounts of unnecessary zooming 0536 // in some situations. We need to do this even when expanding later just in case 0537 // all windows are the same size. 0538 // (We are using an old bounding rect for this, hopefully it doesn't matter) 0539 int xSection = (target_w->x() - bounds.x()) / (bounds.width() / 3); 0540 int ySection = (target_w->y() - bounds.y()) / (bounds.height() / 3); 0541 diff = QPoint(0, 0); 0542 if (xSection != 1 || ySection != 1) { // Remove this if you want the center to pull as well 0543 if (xSection == 1) { 0544 xSection = (directions[cell] / 2 ? 2 : 0); 0545 } 0546 if (ySection == 1) { 0547 ySection = (directions[cell] % 2 ? 2 : 0); 0548 } 0549 } 0550 if (xSection == 0 && ySection == 0) { 0551 diff = QPoint(bounds.topLeft() - target_w->center()); 0552 } 0553 if (xSection == 2 && ySection == 0) { 0554 diff = QPoint(bounds.topRight() - target_w->center()); 0555 } 0556 if (xSection == 2 && ySection == 2) { 0557 diff = QPoint(bounds.bottomRight() - target_w->center()); 0558 } 0559 if (xSection == 0 && ySection == 2) { 0560 diff = QPoint(bounds.bottomLeft() - target_w->center()); 0561 } 0562 if (diff.x() != 0 || diff.y() != 0) { 0563 diff *= m_accuracy / qreal(diff.manhattanLength()); 0564 target_w->translate(diff); 0565 } 0566 0567 // Update bounding rect 0568 bounds = bounds.united(*target_w); 0569 bounds = bounds.united(*target_e); 0570 } 0571 } 0572 } 0573 } while (overlap); 0574 0575 // Compute the scale factor so the bounding rect fits the target area. 0576 qreal scale; 0577 if (bounds.width() <= area.width() && bounds.height() <= area.height()) { 0578 scale = 1.0; 0579 } else if (area.width() / qreal(bounds.width()) < area.height() / qreal(bounds.height())) { 0580 scale = area.width() / qreal(bounds.width()); 0581 } else { 0582 scale = area.height() / qreal(bounds.height()); 0583 } 0584 // Make bounding rect fill the screen size for later steps 0585 bounds = QRect(bounds.x() - (area.width() / scale - bounds.width()) / 2, 0586 bounds.y() - (area.height() / scale - bounds.height()) / 2, 0587 area.width() / scale, 0588 area.height() / scale); 0589 0590 // Move all windows back onto the screen and set their scale 0591 QHash<ExpoCell *, QRect>::iterator target = targets.begin(); 0592 while (target != targets.end()) { 0593 target->setRect((target->x() - bounds.x()) * scale + area.x(), 0594 (target->y() - bounds.y()) * scale + area.y(), 0595 target->width() * scale, 0596 target->height() * scale); 0597 ++target; 0598 } 0599 0600 // Try to fill the gaps by enlarging windows if they have the space 0601 if (m_fillGaps) { 0602 // Don't expand onto or over the border 0603 QRegion borderRegion(area.adjusted(-200, -200, 200, 200)); 0604 borderRegion ^= area; 0605 0606 bool moved; 0607 do { 0608 moved = false; 0609 for (ExpoCell *cell : std::as_const(m_cells)) { 0610 QRect oldRect; 0611 QRect *target = &targets[cell]; 0612 // This may cause some slight distortion if the windows are enlarged a large amount 0613 int widthDiff = m_accuracy; 0614 int heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height(); 0615 int xDiff = widthDiff / 2; // Also move a bit in the direction of the enlarge, allows the 0616 int yDiff = heightDiff / 2; // center windows to be enlarged if there is gaps on the side. 0617 0618 // heightDiff (and yDiff) will be re-computed after each successful enlargement attempt 0619 // so that the error introduced in the window's aspect ratio is minimized 0620 0621 // Attempt enlarging to the top-right 0622 oldRect = *target; 0623 target->setRect(target->x() + xDiff, 0624 target->y() - yDiff - heightDiff, 0625 target->width() + widthDiff, 0626 target->height() + heightDiff); 0627 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) { 0628 *target = oldRect; 0629 } else { 0630 moved = true; 0631 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height(); 0632 yDiff = heightDiff / 2; 0633 } 0634 0635 // Attempt enlarging to the bottom-right 0636 oldRect = *target; 0637 target->setRect(target->x() + xDiff, 0638 target->y() + yDiff, 0639 target->width() + widthDiff, 0640 target->height() + heightDiff); 0641 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) { 0642 *target = oldRect; 0643 } else { 0644 moved = true; 0645 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height(); 0646 yDiff = heightDiff / 2; 0647 } 0648 0649 // Attempt enlarging to the bottom-left 0650 oldRect = *target; 0651 target->setRect(target->x() - xDiff - widthDiff, 0652 target->y() + yDiff, 0653 target->width() + widthDiff, 0654 target->height() + heightDiff); 0655 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) { 0656 *target = oldRect; 0657 } else { 0658 moved = true; 0659 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height(); 0660 yDiff = heightDiff / 2; 0661 } 0662 0663 // Attempt enlarging to the top-left 0664 oldRect = *target; 0665 target->setRect(target->x() - xDiff - widthDiff, 0666 target->y() - yDiff - heightDiff, 0667 target->width() + widthDiff, 0668 target->height() + heightDiff); 0669 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) { 0670 *target = oldRect; 0671 } else { 0672 moved = true; 0673 } 0674 } 0675 } while (moved); 0676 0677 // The expanding code above can actually enlarge windows over 1.0/2.0 scale, we don't like this 0678 // We can't add this to the loop above as it would cause a never-ending loop so we have to make 0679 // do with the less-than-optimal space usage with using this method. 0680 for (ExpoCell *cell : std::as_const(m_cells)) { 0681 QRect *target = &targets[cell]; 0682 qreal scale = target->width() / qreal(cell->naturalWidth()); 0683 if (scale > 2.0 || (scale > 1.0 && (cell->naturalWidth() > 300 || cell->naturalHeight() > 300))) { 0684 scale = (cell->naturalWidth() > 300 || cell->naturalHeight() > 300) ? 1.0 : 2.0; 0685 target->setRect(target->center().x() - int(cell->naturalWidth() * scale) / 2, 0686 target->center().y() - int(cell->naturalHeight() * scale) / 2, 0687 cell->naturalWidth() * scale, 0688 cell->naturalHeight() * scale); 0689 } 0690 } 0691 } 0692 0693 for (ExpoCell *cell : std::as_const(m_cells)) { 0694 const QRect &cellRect = targets.value(cell); 0695 QRect cellRectWithoutMargins = cellRect.marginsRemoved(cell->margins()); 0696 if (!cellRectWithoutMargins.isValid()) { 0697 cellRectWithoutMargins = cellRect; 0698 } 0699 const QRect rect = centered(cell, cellRectWithoutMargins); 0700 0701 cell->setX(rect.x()); 0702 cell->setY(rect.y()); 0703 cell->setWidth(rect.width()); 0704 cell->setHeight(rect.height()); 0705 } 0706 } 0707 0708 void ExpoLayout::resetTransformations() 0709 { 0710 for (ExpoCell *cell : std::as_const(m_cells)) { 0711 cell->setX(cell->naturalX()); 0712 cell->setY(cell->naturalY()); 0713 cell->setWidth(cell->naturalWidth()); 0714 cell->setHeight(cell->naturalHeight()); 0715 } 0716 } 0717 0718 #include "moc_expolayout.cpp"