36 m_layout->removeCell(
this);
39 if (m_layout && m_enabled) {
40 m_layout->addCell(
this);
56 m_layout->addCell(
this);
60 m_layout->removeCell(
this);
81 if (m_naturalX !=
x) {
95 if (m_naturalY !=
y) {
104 return m_naturalWidth;
109 if (m_naturalWidth !=
width) {
110 m_naturalWidth =
width;
118 return m_naturalHeight;
123 if (m_naturalHeight !=
height) {
142 return m_x.value_or(0);
155 return m_y.value_or(0);
168 return m_width.value_or(0);
173 if (m_width !=
width) {
181 return m_height.value_or(0);
194 return m_persistentKey;
199 if (m_persistentKey != key) {
200 m_persistentKey = key;
208 return m_margins.bottom();
213 if (m_margins.bottom() != margin) {
214 m_margins.setBottom(margin);
232 if (m_mode !=
mode) {
246 if (m_fillGaps != fill) {
287 if (!m_cells.isEmpty()) {
290 calculateWindowTransformationsClosest();
293 calculateWindowTransformationsNatural();
296 resetTransformations();
306 Q_ASSERT(!m_cells.contains(cell));
307 m_cells.append(cell);
313 m_cells.removeOne(cell);
319 if (newGeometry.size() != oldGeometry.size()) {
322 QQuickItem::geometryChange(newGeometry, oldGeometry);
325static int distance(
const QPoint &a,
const QPoint &b)
327 const int xdiff = a.x() - b.x();
328 const int ydiff = a.y() - b.y();
329 return int(std::sqrt(qreal(xdiff * xdiff + ydiff * ydiff)));
332static QRect centered(
ExpoCell *cell,
const QRect &bounds)
335 .scaled(bounds.size(), Qt::KeepAspectRatio);
337 return QRect(bounds.center().x() - scaled.width() / 2,
338 bounds.center().y() - scaled.height() / 2,
343void ExpoLayout::calculateWindowTransformationsClosest()
345 QRect area = QRect(0, 0, width(), height());
346 const int columns = int(std::ceil(std::sqrt(qreal(m_cells.count()))));
347 const int rows = int(std::ceil(m_cells.count() / qreal(columns)));
350 const int slotWidth = area.width() / columns;
351 const int slotHeight = area.height() / rows;
352 QList<ExpoCell *> takenSlots;
353 takenSlots.resize(rows * columns);
354 takenSlots.fill(
nullptr);
357 QList<QPoint> slotCenters;
358 slotCenters.resize(rows * columns);
359 for (
int x = 0; x < columns; ++x) {
360 for (
int y = 0; y < rows; ++y) {
361 slotCenters[x + y * columns] = QPoint(area.x() + slotWidth * x + slotWidth / 2,
362 area.y() + slotHeight * y + slotHeight / 2);
367 QList<ExpoCell *> tmpList = m_cells;
368 while (!tmpList.isEmpty()) {
370 int slotCandidate = -1, slotCandidateDistance = INT_MAX;
373 for (
int i = 0; i < columns * rows; ++i) {
374 const int dist = distance(pos, slotCenters[i]);
375 if (
dist < slotCandidateDistance) {
377 Q_ASSERT(occupier != cell);
378 if (!occupier ||
dist < distance(occupier->
naturalRect().center(), slotCenters[i])) {
381 slotCandidateDistance =
dist;
385 Q_ASSERT(slotCandidate != -1);
386 if (takenSlots[slotCandidate]) {
387 tmpList << takenSlots[slotCandidate];
389 tmpList.removeAll(cell);
390 takenSlots[slotCandidate] = cell;
393 for (
int slot = 0; slot < columns * rows; ++slot) {
400 QRect target(area.x() + (slot % columns) * slotWidth,
401 area.y() + (slot / columns) * slotHeight,
402 slotWidth, slotHeight);
403 QRect adjustedTarget = target.adjusted(m_spacing, m_spacing, -m_spacing, -m_spacing);
404 if (adjustedTarget.isValid()) {
405 target = adjustedTarget;
407 target = target.marginsRemoved(cell->
margins());
413 target.moveTop(target.top() + (target.height() -
int(cell->
naturalHeight() * scale)) / 2);
418 target.moveLeft(target.left() + (target.width() -
int(cell->
naturalWidth() * scale)) / 2);
425 target.center().x() -
int(cell->
naturalWidth() * scale) / 2,
426 target.center().y() -
int(cell->
naturalHeight() * scale) / 2,
430 cell->
setX(target.x());
431 cell->
setY(target.y());
437static inline int heightForWidth(
ExpoCell *cell,
int width)
442static bool isOverlappingAny(
ExpoCell *w,
const QHash<ExpoCell *, QRect> &targets,
const QRegion &border,
int spacing)
444 QHash<ExpoCell *, QRect>::const_iterator winTarget = targets.find(w);
445 if (winTarget == targets.constEnd()) {
448 if (border.intersects(*winTarget)) {
451 const QMargins halfSpacing(spacing / 2, spacing / 2, spacing / 2, spacing / 2);
454 QHash<ExpoCell *, QRect>::const_iterator target;
455 for (target = targets.constBegin(); target != targets.constEnd(); ++target) {
456 if (target == winTarget) {
459 if (winTarget->marginsAdded(halfSpacing).intersects(target->marginsAdded(halfSpacing))) {
466void ExpoLayout::calculateWindowTransformationsNatural()
468 const QRect area = QRect(0, 0, width(), height());
472 std::sort(m_cells.begin(), m_cells.end(), [](
const ExpoCell *a,
const ExpoCell *b) {
473 return a->persistentKey() < b->persistentKey();
478 QHash<ExpoCell *, QRect> targets;
479 QHash<ExpoCell *, int> directions;
481 for (
ExpoCell *cell : std::as_const(m_cells)) {
483 targets[cell] = cellRect;
486 directions[cell] = direction;
487 bounds = bounds.united(cellRect);
489 if (direction == 4) {
496 const int halfSpacing = m_spacing / 2;
500 for (
ExpoCell *cell : std::as_const(m_cells)) {
501 QRect *target_w = &targets[cell];
502 for (
ExpoCell *e : std::as_const(m_cells)) {
507 QRect *target_e = &targets[e];
508 if (target_w->adjusted(-halfSpacing, -halfSpacing, halfSpacing, halfSpacing)
509 .intersects(target_e->adjusted(-halfSpacing, -halfSpacing, halfSpacing, halfSpacing))) {
513 QPoint diff(target_e->center() - target_w->center());
515 if (diff.x() == 0 && diff.y() == 0) {
524 diff *= m_accuracy / qreal(diff.manhattanLength());
526 target_w->translate(-diff);
527 target_e->translate(diff);
539 int xSection = (target_w->x() - bounds.x()) / (bounds.width() / 3);
540 int ySection = (target_w->y() - bounds.y()) / (bounds.height() / 3);
542 if (xSection != 1 || ySection != 1) {
544 xSection = (directions[cell] / 2 ? 2 : 0);
547 ySection = (directions[cell] % 2 ? 2 : 0);
550 if (xSection == 0 && ySection == 0) {
551 diff = QPoint(bounds.topLeft() - target_w->center());
553 if (xSection == 2 && ySection == 0) {
554 diff = QPoint(bounds.topRight() - target_w->center());
556 if (xSection == 2 && ySection == 2) {
557 diff = QPoint(bounds.bottomRight() - target_w->center());
559 if (xSection == 0 && ySection == 2) {
560 diff = QPoint(bounds.bottomLeft() - target_w->center());
562 if (diff.x() != 0 || diff.y() != 0) {
563 diff *= m_accuracy / qreal(diff.manhattanLength());
564 target_w->translate(diff);
568 bounds = bounds.united(*target_w);
569 bounds = bounds.united(*target_e);
577 if (bounds.width() <= area.width() && bounds.height() <= area.height()) {
579 }
else if (area.width() / qreal(bounds.width()) < area.height() / qreal(bounds.height())) {
580 scale = area.width() / qreal(bounds.width());
582 scale = area.height() / qreal(bounds.height());
585 bounds = QRect(bounds.x() - (area.width() / scale - bounds.width()) / 2,
586 bounds.y() - (area.height() / scale - bounds.height()) / 2,
587 area.width() / scale,
588 area.height() / scale);
591 QHash<ExpoCell *, QRect>::iterator target = targets.begin();
592 while (target != targets.end()) {
593 target->setRect((target->x() - bounds.x()) * scale + area.x(),
594 (target->y() - bounds.y()) * scale + area.y(),
595 target->width() * scale,
596 target->height() * scale);
603 QRegion borderRegion(area.adjusted(-200, -200, 200, 200));
604 borderRegion ^= area;
609 for (
ExpoCell *cell : std::as_const(m_cells)) {
611 QRect *target = &targets[cell];
613 int widthDiff = m_accuracy;
614 int heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height();
615 int xDiff = widthDiff / 2;
616 int yDiff = heightDiff / 2;
623 target->setRect(target->x() + xDiff,
624 target->y() - yDiff - heightDiff,
625 target->width() + widthDiff,
626 target->height() + heightDiff);
627 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) {
631 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height();
632 yDiff = heightDiff / 2;
637 target->setRect(target->x() + xDiff,
639 target->width() + widthDiff,
640 target->height() + heightDiff);
641 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) {
645 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height();
646 yDiff = heightDiff / 2;
651 target->setRect(target->x() - xDiff - widthDiff,
653 target->width() + widthDiff,
654 target->height() + heightDiff);
655 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) {
659 heightDiff = heightForWidth(cell, target->width() + widthDiff) - target->height();
660 yDiff = heightDiff / 2;
665 target->setRect(target->x() - xDiff - widthDiff,
666 target->y() - yDiff - heightDiff,
667 target->width() + widthDiff,
668 target->height() + heightDiff);
669 if (isOverlappingAny(cell, targets, borderRegion, m_spacing)) {
680 for (
ExpoCell *cell : std::as_const(m_cells)) {
681 QRect *target = &targets[cell];
682 qreal scale = target->width() / qreal(cell->
naturalWidth());
685 target->setRect(target->center().x() -
int(cell->
naturalWidth() * scale) / 2,
686 target->center().y() -
int(cell->
naturalHeight() * scale) / 2,
693 for (
ExpoCell *cell : std::as_const(m_cells)) {
694 const QRect &cellRect = targets.value(cell);
695 QRect cellRectWithoutMargins = cellRect.marginsRemoved(cell->
margins());
696 if (!cellRectWithoutMargins.isValid()) {
697 cellRectWithoutMargins = cellRect;
699 const QRect rect = centered(cell, cellRectWithoutMargins);
701 cell->
setX(rect.x());
702 cell->
setY(rect.y());
708void ExpoLayout::resetTransformations()
710 for (
ExpoCell *cell : std::as_const(m_cells)) {
718#include "moc_expolayout.cpp"
void bottomMarginChanged()
void naturalHeightChanged()
void persistentKeyChanged()
void setHeight(int height)
ExpoCell(QObject *parent=nullptr)
void naturalWidthChanged()
void setNaturalHeight(int height)
void setNaturalWidth(int width)
void setBottomMargin(int margin)
void setLayout(ExpoLayout *layout)
QRect naturalRect() const
void setEnabled(bool enabled)
void setPersistentKey(const QString &key)
Q_INVOKABLE void forceLayout()
ExpoLayout(QQuickItem *parent=nullptr)
void setFillGaps(bool fill)
void setMode(LayoutMode mode)
void removeCell(ExpoCell *cell)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void updatePolish() override
void addCell(ExpoCell *cell)
void setSpacing(int spacing)