Make lxqt globals a public header
[lxde/liblxqt.git] / lxqtgridlayout.cpp
CommitLineData
ca1b12f4
AS
1/* BEGIN_COMMON_COPYRIGHT_HEADER
2 * (c)LGPL2+
3 *
b9223fe7 4 * LXQt - a lightweight, Qt based, desktop toolset
ca1b12f4
AS
5 * http://razor-qt.org
6 *
7 * Copyright: 2012 Razor team
8 * Authors:
9 * Alexander Sokoloff <sokoloff.a@gmail.com>
10 *
11 * This program or library is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20
21 * You should have received a copy of the GNU Lesser General
22 * Public License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301 USA
25 *
26 * END_COMMON_COPYRIGHT_HEADER */
27
28
f9de00b4 29#include "lxqtgridlayout.h"
e6389bfc 30#include <QDebug>
ca1b12f4 31#include <math.h>
afd3b116 32#include <QWidget>
c6a4f226 33#include <QVariantAnimation>
afd3b116 34
f05ba5af 35using namespace LXQt;
f9de00b4 36
19c373de
PK
37class LXQt::GridLayoutPrivate
38{
39public:
40 GridLayoutPrivate();
41 ~GridLayoutPrivate();
42
43 QList<QLayoutItem*> mItems;
44 int mRowCount;
45 int mColumnCount;
46 GridLayout::Direction mDirection;
47
48 bool mIsValid;
49 QSize mCellSizeHint;
50 QSize mCellMaxSize;
51 int mVisibleCount;
52 GridLayout::Stretch mStretch;
53 bool mAnimate;
303190cf 54 int mAnimatedItems; //!< counter of currently animated items
19c373de
PK
55
56
57 void updateCache();
58 int rows() const;
59 int cols() const;
60 void setItemGeometry(QLayoutItem * item, QRect const & geometry);
61 QSize mPrefCellMinSize;
62 QSize mPrefCellMaxSize;
63 QRect mOccupiedGeometry;
64};
65
c6a4f226
PK
66namespace
67{
68 class ItemMoveAnimation : public QVariantAnimation
69 {
70 public:
303190cf 71 static void animate(QLayoutItem * item, QRect const & geometry, LXQt::GridLayoutPrivate * layout)
c6a4f226
PK
72 {
73 ItemMoveAnimation* animation = new ItemMoveAnimation(item);
74 animation->setStartValue(item->geometry());
75 animation->setEndValue(geometry);
303190cf
PK
76 ++layout->mAnimatedItems;
77 connect(animation, &QAbstractAnimation::finished, [layout] { --layout->mAnimatedItems; Q_ASSERT(0 <= layout->mAnimatedItems); });
c6a4f226
PK
78 animation->start(DeleteWhenStopped);
79 }
80
81 ItemMoveAnimation(QLayoutItem *item)
82 : mItem(item)
83 {
32b87026 84 setDuration(150);
c6a4f226
PK
85 }
86
87 void updateCurrentValue(const QVariant &current)
88 {
89 mItem->setGeometry(current.toRect());
90 }
91
92 private:
93 QLayoutItem* mItem;
94
95 };
96}
97
ca1b12f4
AS
98
99/************************************************
100
101 ************************************************/
f9de00b4 102GridLayoutPrivate::GridLayoutPrivate()
a654e583
AS
103{
104 mColumnCount = 0;
105 mRowCount = 0;
f9de00b4 106 mDirection = GridLayout::LeftToRight;
a654e583
AS
107 mIsValid = false;
108 mVisibleCount = 0;
f9de00b4 109 mStretch = GridLayout::StretchHorizontal | GridLayout::StretchVertical;
c6a4f226 110 mAnimate = false;
303190cf 111 mAnimatedItems = 0;
a654e583
AS
112 mPrefCellMinSize = QSize(0,0);
113 mPrefCellMaxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
114}
115
4192a2d4
PK
116/************************************************
117
118 ************************************************/
119GridLayoutPrivate::~GridLayoutPrivate()
120{
121 qDeleteAll(mItems);
122}
123
a654e583
AS
124
125/************************************************
126
127 ************************************************/
f9de00b4 128void GridLayoutPrivate::updateCache()
ca1b12f4 129{
40e2257e
AS
130 mCellSizeHint = QSize(0, 0);
131 mCellMaxSize = QSize(0, 0);
ca1b12f4
AS
132 mVisibleCount = 0;
133
8dd17d46
LP
134 const int N = mItems.count();
135 for (int i=0; i < N; ++i)
ca1b12f4
AS
136 {
137 QLayoutItem *item = mItems.at(i);
d32e88ab 138 if (!item->widget() || item->widget()->isHidden())
ca1b12f4
AS
139 continue;
140
141 int h = qBound(item->minimumSize().height(),
142 item->sizeHint().height(),
143 item->maximumSize().height());
144
145 int w = qBound(item->minimumSize().width(),
146 item->sizeHint().width(),
147 item->maximumSize().width());
148
ca1b12f4
AS
149 mCellSizeHint.rheight() = qMax(mCellSizeHint.height(), h);
150 mCellSizeHint.rwidth() = qMax(mCellSizeHint.width(), w);
151
152 mCellMaxSize.rheight() = qMax(mCellMaxSize.height(), item->maximumSize().height());
153 mCellMaxSize.rwidth() = qMax(mCellMaxSize.width(), item->maximumSize().width());
154 mVisibleCount++;
40e2257e
AS
155
156#if 0
157 qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
158 qDebug() << "item.min" << item->minimumSize().width();
159 qDebug() << "item.sz " << item->sizeHint().width();
160 qDebug() << "item.max" << item->maximumSize().width();
161 qDebug() << "w h" << w << h;
162 qDebug() << "wid.sizeHint" << item->widget()->sizeHint();
163 qDebug() << "mCellSizeHint:" << mCellSizeHint;
164 qDebug() << "mCellMaxSize: " << mCellMaxSize;
165 qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
166#endif
167
ca1b12f4 168 }
a654e583
AS
169 mCellSizeHint.rwidth() = qBound(mPrefCellMinSize.width(), mCellSizeHint.width(), mPrefCellMaxSize.width());
170 mCellSizeHint.rheight()= qBound(mPrefCellMinSize.height(), mCellSizeHint.height(), mPrefCellMaxSize.height());
40e2257e 171 mIsValid = !mCellSizeHint.isEmpty();
ca1b12f4
AS
172}
173
174
175/************************************************
176
177 ************************************************/
f9de00b4 178int GridLayoutPrivate::rows() const
ca1b12f4
AS
179{
180 if (mRowCount)
181 return mRowCount;
182
183 if (!mColumnCount)
184 return 1;
185
186 return ceil(mVisibleCount * 1.0 / mColumnCount);
187}
188
189
190/************************************************
191
192 ************************************************/
f9de00b4 193int GridLayoutPrivate::cols() const
ca1b12f4
AS
194{
195 if (mColumnCount)
196 return mColumnCount;
197
198 int rows = mRowCount;
199 if (!rows)
200 rows = 1;
201
202 return ceil(mVisibleCount * 1.0 / rows);
203}
204
c6a4f226
PK
205void GridLayoutPrivate::setItemGeometry(QLayoutItem * item, QRect const & geometry)
206{
57f016d9 207 mOccupiedGeometry |= geometry;
c6a4f226
PK
208 if (mAnimate)
209 {
303190cf 210 ItemMoveAnimation::animate(item, geometry, this);
c6a4f226
PK
211 } else
212 {
213 item->setGeometry(geometry);
214 }
215}
ca1b12f4
AS
216
217
218/************************************************
219
220 ************************************************/
f9de00b4 221GridLayout::GridLayout(QWidget *parent):
ca1b12f4 222 QLayout(parent),
f9de00b4 223 d_ptr(new GridLayoutPrivate())
ca1b12f4 224{
ca1b12f4
AS
225}
226
227
228/************************************************
229
230 ************************************************/
f9de00b4 231GridLayout::~GridLayout()
ca1b12f4
AS
232{
233 delete d_ptr;
234}
235
236
237/************************************************
238
239 ************************************************/
f9de00b4 240void GridLayout::addItem(QLayoutItem *item)
ca1b12f4
AS
241{
242 d_ptr->mItems.append(item);
243}
244
245
246/************************************************
247
248 ************************************************/
f9de00b4 249QLayoutItem *GridLayout::itemAt(int index) const
ca1b12f4 250{
f9de00b4 251 Q_D(const GridLayout);
ca1b12f4
AS
252 if (index < 0 || index >= d->mItems.count())
253 return 0;
254
255 return d->mItems.at(index);
256}
257
258
259/************************************************
260
261 ************************************************/
f9de00b4 262QLayoutItem *GridLayout::takeAt(int index)
ca1b12f4 263{
f9de00b4 264 Q_D(GridLayout);
ca1b12f4
AS
265 if (index < 0 || index >= d->mItems.count())
266 return 0;
267
268 QLayoutItem *item = d->mItems.takeAt(index);
269 return item;
270}
271
272
273/************************************************
274
275 ************************************************/
f9de00b4 276int GridLayout::count() const
ca1b12f4 277{
f9de00b4 278 Q_D(const GridLayout);
ca1b12f4
AS
279 return d->mItems.count();
280}
281
282
283/************************************************
284
285 ************************************************/
f9de00b4 286void GridLayout::invalidate()
ca1b12f4 287{
f9de00b4 288 Q_D(GridLayout);
ca1b12f4
AS
289 d->mIsValid = false;
290 QLayout::invalidate();
291}
292
293
294/************************************************
295
296 ************************************************/
f9de00b4 297int GridLayout::rowCount() const
ca1b12f4 298{
f9de00b4 299 Q_D(const GridLayout);
ca1b12f4
AS
300 return d->mRowCount;
301}
302
303
304/************************************************
305
306 ************************************************/
f9de00b4 307void GridLayout::setRowCount(int value)
ca1b12f4 308{
f9de00b4 309 Q_D(GridLayout);
ca1b12f4
AS
310 if (d->mRowCount != value)
311 {
312 d->mRowCount = value;
313 invalidate();
314 }
315}
316
317
318/************************************************
319
320 ************************************************/
f9de00b4 321int GridLayout::columnCount() const
ca1b12f4 322{
f9de00b4 323 Q_D(const GridLayout);
ca1b12f4
AS
324 return d->mColumnCount;
325}
326
327
328/************************************************
329
330 ************************************************/
f9de00b4 331void GridLayout::setColumnCount(int value)
ca1b12f4 332{
f9de00b4 333 Q_D(GridLayout);
ca1b12f4
AS
334 if (d->mColumnCount != value)
335 {
336 d->mColumnCount = value;
337 invalidate();
338 }
339}
340
341
342/************************************************
343
344 ************************************************/
f9de00b4 345GridLayout::Direction GridLayout::direction() const
ca1b12f4 346{
f9de00b4 347 Q_D(const GridLayout);
ca1b12f4
AS
348 return d->mDirection;
349}
350
351
352/************************************************
353
354 ************************************************/
f9de00b4 355void GridLayout::setDirection(GridLayout::Direction value)
ca1b12f4 356{
f9de00b4 357 Q_D(GridLayout);
ca1b12f4
AS
358 if (d->mDirection != value)
359 {
360 d->mDirection = value;
361 invalidate();
362 }
363}
364
365/************************************************
366
367 ************************************************/
f9de00b4 368GridLayout::Stretch GridLayout::stretch() const
ca1b12f4 369{
f9de00b4 370 Q_D(const GridLayout);
ca1b12f4
AS
371 return d->mStretch;
372}
373
374/************************************************
375
376 ************************************************/
f9de00b4 377void GridLayout::setStretch(Stretch value)
ca1b12f4 378{
f9de00b4 379 Q_D(GridLayout);
ca1b12f4
AS
380 if (d->mStretch != value)
381 {
382 d->mStretch = value;
383 invalidate();
384 }
385}
386
387
388/************************************************
5a1ce682
AS
389
390 ************************************************/
c6a4f226 391void GridLayout::moveItem(int from, int to, bool withAnimation /*= false*/)
5a1ce682 392{
f9de00b4 393 Q_D(GridLayout);
c6a4f226 394 d->mAnimate = withAnimation;
5a1ce682
AS
395 d->mItems.move(from, to);
396 invalidate();
397}
398
399
400/************************************************
ca1b12f4
AS
401
402 ************************************************/
303190cf
PK
403bool GridLayout::animatedMoveInProgress() const
404{
405 Q_D(const GridLayout);
406 return 0 < d->mAnimatedItems;
407}
408
409
410/************************************************
411
412 ************************************************/
f9de00b4 413QSize GridLayout::cellMinimumSize() const
a654e583 414{
f9de00b4 415 Q_D(const GridLayout);
a654e583
AS
416 return d->mPrefCellMinSize;
417}
418
419
420/************************************************
421
422 ************************************************/
f9de00b4 423void GridLayout::setCellMinimumSize(QSize minSize)
a654e583 424{
f9de00b4 425 Q_D(GridLayout);
a654e583
AS
426 if (d->mPrefCellMinSize != minSize)
427 {
428 d->mPrefCellMinSize = minSize;
429 invalidate();
430 }
431}
432
433
434/************************************************
435
436 ************************************************/
f9de00b4 437void GridLayout::setCellMinimumHeight(int value)
05a1e105 438{
f9de00b4 439 Q_D(GridLayout);
05a1e105
AS
440 if (d->mPrefCellMinSize.height() != value)
441 {
442 d->mPrefCellMinSize.setHeight(value);
443 invalidate();
444 }
445}
446
447
448/************************************************
449
450 ************************************************/
f9de00b4 451void GridLayout::setCellMinimumWidth(int value)
05a1e105 452{
f9de00b4 453 Q_D(GridLayout);
05a1e105
AS
454 if (d->mPrefCellMinSize.width() != value)
455 {
456 d->mPrefCellMinSize.setWidth(value);
457 invalidate();
458 }
459}
460
461
462/************************************************
463
464 ************************************************/
f9de00b4 465QSize GridLayout::cellMaximumSize() const
a654e583 466{
f9de00b4 467 Q_D(const GridLayout);
a654e583
AS
468 return d->mPrefCellMaxSize;
469}
470
471
472/************************************************
473
474 ************************************************/
f9de00b4 475void GridLayout::setCellMaximumSize(QSize maxSize)
a654e583 476{
f9de00b4 477 Q_D(GridLayout);
a654e583
AS
478 if (d->mPrefCellMaxSize != maxSize)
479 {
480 d->mPrefCellMaxSize = maxSize;
481 invalidate();
482 }
483}
484
485
486/************************************************
487
488 ************************************************/
f9de00b4 489void GridLayout::setCellMaximumHeight(int value)
05a1e105 490{
f9de00b4 491 Q_D(GridLayout);
05a1e105
AS
492 if (d->mPrefCellMaxSize.height() != value)
493 {
494 d->mPrefCellMaxSize.setHeight(value);
495 invalidate();
496 }
497}
498
499
500/************************************************
501
502 ************************************************/
f9de00b4 503void GridLayout::setCellMaximumWidth(int value)
05a1e105 504{
f9de00b4 505 Q_D(GridLayout);
05a1e105
AS
506 if (d->mPrefCellMaxSize.width() != value)
507 {
508 d->mPrefCellMaxSize.setWidth(value);
509 invalidate();
510 }
511}
512
513
514/************************************************
515
516 ************************************************/
f9de00b4 517void GridLayout::setCellFixedSize(QSize size)
05a1e105 518{
f9de00b4 519 Q_D(GridLayout);
05a1e105
AS
520 if (d->mPrefCellMinSize != size ||
521 d->mPrefCellMaxSize != size)
522 {
523 d->mPrefCellMinSize = size;
524 d->mPrefCellMaxSize = size;
525 invalidate();
526 }
527}
528
529
530/************************************************
531
532 ************************************************/
f9de00b4 533void GridLayout::setCellFixedHeight(int value)
05a1e105 534{
f9de00b4 535 Q_D(GridLayout);
05a1e105
AS
536 if (d->mPrefCellMinSize.height() != value ||
537 d->mPrefCellMaxSize.height() != value)
538 {
539 d->mPrefCellMinSize.setHeight(value);
540 d->mPrefCellMaxSize.setHeight(value);
541 invalidate();
542 }
543}
544
545
546/************************************************
547
548 ************************************************/
f9de00b4 549void GridLayout::setCellFixedWidth(int value)
05a1e105 550{
f9de00b4 551 Q_D(GridLayout);
05a1e105
AS
552 if (d->mPrefCellMinSize.width() != value ||
553 d->mPrefCellMaxSize.width() != value)
554 {
555 d->mPrefCellMinSize.setWidth(value);
556 d->mPrefCellMaxSize.setWidth(value);
557 invalidate();
558 }
559}
560
561
562/************************************************
563
564 ************************************************/
f9de00b4 565QSize GridLayout::sizeHint() const
ca1b12f4 566{
f9de00b4 567 Q_D(const GridLayout);
ca1b12f4
AS
568
569 if (!d->mIsValid)
f9de00b4 570 const_cast<GridLayoutPrivate*>(d)->updateCache();
ca1b12f4
AS
571
572 return QSize(d->cols() * d->mCellSizeHint.width(),
573 d->rows() * d->mCellSizeHint.height());
574}
575
576
577/************************************************
578
579 ************************************************/
f9de00b4 580void GridLayout::setGeometry(const QRect &geometry)
ca1b12f4 581{
f9de00b4 582 Q_D(GridLayout);
ca1b12f4 583
924c204c
PK
584 const bool visual_h_reversed = parentWidget() && parentWidget()->isRightToLeft();
585
7e4fa359 586 QLayout::setGeometry(geometry);
924c204c
PK
587 const QPoint occupied_start = visual_h_reversed ? geometry.topRight() : geometry.topLeft();
588 d->mOccupiedGeometry.setTopLeft(occupied_start);
589 d->mOccupiedGeometry.setBottomRight(occupied_start);
7e4fa359 590
ca1b12f4
AS
591 if (!d->mIsValid)
592 d->updateCache();
593
594 int y = geometry.top();
595 int x = geometry.left();
596
a654e583
AS
597 // For historical reasons QRect::right returns left() + width() - 1
598 // and QRect::bottom() returns top() + height() - 1;
599 // So we use left() + height() and top() + height()
600 //
601 // http://qt-project.org/doc/qt-4.8/qrect.html
602
1dfeb572
PK
603 const int maxX = geometry.left() + geometry.width();
604 const int maxY = geometry.top() + geometry.height();
a654e583 605
1dfeb572
PK
606 const bool stretch_h = d->mStretch.testFlag(StretchHorizontal);
607 const bool stretch_v = d->mStretch.testFlag(StretchVertical);
a654e583 608
1dfeb572 609 const int cols = d->cols();
82a65ae8
PK
610 int itemWidth = 0;
611 if (stretch_h && 0 < cols)
612 itemWidth = qMin(geometry.width() / cols, d->mCellMaxSize.width());
613 else
614 itemWidth = d->mCellSizeHint.width();
615 itemWidth = qBound(qMin(d->mPrefCellMinSize.width(), maxX), itemWidth, d->mPrefCellMaxSize.width());
616 const int widthRemain = stretch_h && 0 < itemWidth ? geometry.width() % itemWidth : 0;
1dfeb572
PK
617
618 const int rows = d->rows();
82a65ae8
PK
619 int itemHeight = 0;
620 if (stretch_v && 0 < rows)
621 itemHeight = qMin(geometry.height() / rows, d->mCellMaxSize.height());
622 else
623 itemHeight = d->mCellSizeHint.height();
624 itemHeight = qBound(qMin(d->mPrefCellMinSize.height(), maxY), itemHeight, d->mPrefCellMaxSize.height());
625 const int heightRemain = stretch_v && 0 < itemHeight ? geometry.height() % itemHeight : 0;
a654e583 626
ca1b12f4 627#if 0
f9de00b4 628 qDebug() << "** GridLayout::setGeometry *******************************";
a654e583
AS
629 qDebug() << "Geometry:" << geometry;
630 qDebug() << "CellSize:" << d->mCellSizeHint;
631 qDebug() << "Constraints:" << "min" << d->mPrefCellMinSize << "max" << d->mPrefCellMaxSize;
40e2257e 632 qDebug() << "Count" << count();
ca1b12f4
AS
633 qDebug() << "Cols:" << d->cols() << "(" << d->mColumnCount << ")";
634 qDebug() << "Rows:" << d->rows() << "(" << d->mRowCount << ")";
9604060d 635 qDebug() << "Stretch:" << "h:" << (d->mStretch.testFlag(StretchHorizontal)) << " v:" << (d->mStretch.testFlag(StretchVertical));
ca1b12f4
AS
636 qDebug() << "Item:" << "h:" << itemHeight << " w:" << itemWidth;
637#endif
638
82a65ae8
PK
639 int remain_height = heightRemain;
640 int remain_width = widthRemain;
ca1b12f4 641 if (d->mDirection == LeftToRight)
dac4466a 642 {
82a65ae8 643 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
e90b2212 644 for (QLayoutItem *item : qAsConst(d->mItems))
ca1b12f4 645 {
d32e88ab 646 if (!item->widget() || item->widget()->isHidden())
ca1b12f4 647 continue;
82a65ae8 648 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
a654e583 649
82a65ae8 650 if (x + width > maxX)
ca1b12f4
AS
651 {
652 x = geometry.left();
82a65ae8 653 y += height;
ca1b12f4 654
82a65ae8
PK
655 height = itemHeight + (0 < remain_height-- ? 1 : 0);
656 remain_width = widthRemain;
ca1b12f4
AS
657 }
658
924c204c
PK
659 const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x;
660 d->setItemGeometry(item, QRect(left, y, width, height));
82a65ae8 661 x += width;
ca1b12f4
AS
662 }
663 }
664 else
665 {
82a65ae8 666 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
e90b2212 667 for (QLayoutItem *item : qAsConst(d->mItems))
ca1b12f4 668 {
d32e88ab 669 if (!item->widget() || item->widget()->isHidden())
ca1b12f4 670 continue;
82a65ae8 671 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
ca1b12f4 672
82a65ae8 673 if (y + height > maxY)
ca1b12f4
AS
674 {
675 y = geometry.top();
82a65ae8 676 x += width;
ca1b12f4 677
82a65ae8
PK
678 width = itemWidth + (0 < remain_width-- ? 1 : 0);
679 remain_height = heightRemain;
ca1b12f4 680 }
924c204c
PK
681 const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x;
682 d->setItemGeometry(item, QRect(left, y, width, height));
82a65ae8 683 y += height;
ca1b12f4
AS
684 }
685 }
c6a4f226 686 d->mAnimate = false;
ca1b12f4
AS
687}
688
57f016d9
PK
689/************************************************
690
691 ************************************************/
692QRect GridLayout::occupiedGeometry() const
693{
694 return d_func()->mOccupiedGeometry;
695}