Removes unneeded semicolon in namespace definition
[lxde/liblxqt.git] / lxqtgridlayout.cpp
1 /* BEGIN_COMMON_COPYRIGHT_HEADER
2 * (c)LGPL2+
3 *
4 * LXQt - a lightweight, Qt based, desktop toolset
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
29 #include "lxqtgridlayout.h"
30 #include <QDebug>
31 #include <math.h>
32 #include <QWidget>
33 #include <QVariantAnimation>
34
35 using namespace LXQt;
36
37 class LXQt::GridLayoutPrivate
38 {
39 public:
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;
54 int mAnimatedItems; //!< counter of currently animated items
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
66 namespace
67 {
68 class ItemMoveAnimation : public QVariantAnimation
69 {
70 public:
71 static void animate(QLayoutItem * item, QRect const & geometry, LXQt::GridLayoutPrivate * layout)
72 {
73 ItemMoveAnimation* animation = new ItemMoveAnimation(item);
74 animation->setStartValue(item->geometry());
75 animation->setEndValue(geometry);
76 ++layout->mAnimatedItems;
77 connect(animation, &QAbstractAnimation::finished, [layout] { --layout->mAnimatedItems; Q_ASSERT(0 <= layout->mAnimatedItems); });
78 animation->start(DeleteWhenStopped);
79 }
80
81 ItemMoveAnimation(QLayoutItem *item)
82 : mItem(item)
83 {
84 setDuration(150);
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
98
99 /************************************************
100
101 ************************************************/
102 GridLayoutPrivate::GridLayoutPrivate()
103 {
104 mColumnCount = 0;
105 mRowCount = 0;
106 mDirection = GridLayout::LeftToRight;
107 mIsValid = false;
108 mVisibleCount = 0;
109 mStretch = GridLayout::StretchHorizontal | GridLayout::StretchVertical;
110 mAnimate = false;
111 mAnimatedItems = 0;
112 mPrefCellMinSize = QSize(0,0);
113 mPrefCellMaxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
114 }
115
116 /************************************************
117
118 ************************************************/
119 GridLayoutPrivate::~GridLayoutPrivate()
120 {
121 qDeleteAll(mItems);
122 }
123
124
125 /************************************************
126
127 ************************************************/
128 void GridLayoutPrivate::updateCache()
129 {
130 mCellSizeHint = QSize(0, 0);
131 mCellMaxSize = QSize(0, 0);
132 mVisibleCount = 0;
133
134 const int N = mItems.count();
135 for (int i=0; i < N; ++i)
136 {
137 QLayoutItem *item = mItems.at(i);
138 if (!item->widget() || item->widget()->isHidden())
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
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++;
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
168 }
169 mCellSizeHint.rwidth() = qBound(mPrefCellMinSize.width(), mCellSizeHint.width(), mPrefCellMaxSize.width());
170 mCellSizeHint.rheight()= qBound(mPrefCellMinSize.height(), mCellSizeHint.height(), mPrefCellMaxSize.height());
171 mIsValid = !mCellSizeHint.isEmpty();
172 }
173
174
175 /************************************************
176
177 ************************************************/
178 int GridLayoutPrivate::rows() const
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 ************************************************/
193 int GridLayoutPrivate::cols() const
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
205 void GridLayoutPrivate::setItemGeometry(QLayoutItem * item, QRect const & geometry)
206 {
207 mOccupiedGeometry |= geometry;
208 if (mAnimate)
209 {
210 ItemMoveAnimation::animate(item, geometry, this);
211 } else
212 {
213 item->setGeometry(geometry);
214 }
215 }
216
217
218 /************************************************
219
220 ************************************************/
221 GridLayout::GridLayout(QWidget *parent):
222 QLayout(parent),
223 d_ptr(new GridLayoutPrivate())
224 {
225 }
226
227
228 /************************************************
229
230 ************************************************/
231 GridLayout::~GridLayout()
232 {
233 delete d_ptr;
234 }
235
236
237 /************************************************
238
239 ************************************************/
240 void GridLayout::addItem(QLayoutItem *item)
241 {
242 d_ptr->mItems.append(item);
243 }
244
245
246 /************************************************
247
248 ************************************************/
249 QLayoutItem *GridLayout::itemAt(int index) const
250 {
251 Q_D(const GridLayout);
252 if (index < 0 || index >= d->mItems.count())
253 return 0;
254
255 return d->mItems.at(index);
256 }
257
258
259 /************************************************
260
261 ************************************************/
262 QLayoutItem *GridLayout::takeAt(int index)
263 {
264 Q_D(GridLayout);
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 ************************************************/
276 int GridLayout::count() const
277 {
278 Q_D(const GridLayout);
279 return d->mItems.count();
280 }
281
282
283 /************************************************
284
285 ************************************************/
286 void GridLayout::invalidate()
287 {
288 Q_D(GridLayout);
289 d->mIsValid = false;
290 QLayout::invalidate();
291 }
292
293
294 /************************************************
295
296 ************************************************/
297 int GridLayout::rowCount() const
298 {
299 Q_D(const GridLayout);
300 return d->mRowCount;
301 }
302
303
304 /************************************************
305
306 ************************************************/
307 void GridLayout::setRowCount(int value)
308 {
309 Q_D(GridLayout);
310 if (d->mRowCount != value)
311 {
312 d->mRowCount = value;
313 invalidate();
314 }
315 }
316
317
318 /************************************************
319
320 ************************************************/
321 int GridLayout::columnCount() const
322 {
323 Q_D(const GridLayout);
324 return d->mColumnCount;
325 }
326
327
328 /************************************************
329
330 ************************************************/
331 void GridLayout::setColumnCount(int value)
332 {
333 Q_D(GridLayout);
334 if (d->mColumnCount != value)
335 {
336 d->mColumnCount = value;
337 invalidate();
338 }
339 }
340
341
342 /************************************************
343
344 ************************************************/
345 GridLayout::Direction GridLayout::direction() const
346 {
347 Q_D(const GridLayout);
348 return d->mDirection;
349 }
350
351
352 /************************************************
353
354 ************************************************/
355 void GridLayout::setDirection(GridLayout::Direction value)
356 {
357 Q_D(GridLayout);
358 if (d->mDirection != value)
359 {
360 d->mDirection = value;
361 invalidate();
362 }
363 }
364
365 /************************************************
366
367 ************************************************/
368 GridLayout::Stretch GridLayout::stretch() const
369 {
370 Q_D(const GridLayout);
371 return d->mStretch;
372 }
373
374 /************************************************
375
376 ************************************************/
377 void GridLayout::setStretch(Stretch value)
378 {
379 Q_D(GridLayout);
380 if (d->mStretch != value)
381 {
382 d->mStretch = value;
383 invalidate();
384 }
385 }
386
387
388 /************************************************
389
390 ************************************************/
391 void GridLayout::moveItem(int from, int to, bool withAnimation /*= false*/)
392 {
393 Q_D(GridLayout);
394 d->mAnimate = withAnimation;
395 d->mItems.move(from, to);
396 invalidate();
397 }
398
399
400 /************************************************
401
402 ************************************************/
403 bool GridLayout::animatedMoveInProgress() const
404 {
405 Q_D(const GridLayout);
406 return 0 < d->mAnimatedItems;
407 }
408
409
410 /************************************************
411
412 ************************************************/
413 QSize GridLayout::cellMinimumSize() const
414 {
415 Q_D(const GridLayout);
416 return d->mPrefCellMinSize;
417 }
418
419
420 /************************************************
421
422 ************************************************/
423 void GridLayout::setCellMinimumSize(QSize minSize)
424 {
425 Q_D(GridLayout);
426 if (d->mPrefCellMinSize != minSize)
427 {
428 d->mPrefCellMinSize = minSize;
429 invalidate();
430 }
431 }
432
433
434 /************************************************
435
436 ************************************************/
437 void GridLayout::setCellMinimumHeight(int value)
438 {
439 Q_D(GridLayout);
440 if (d->mPrefCellMinSize.height() != value)
441 {
442 d->mPrefCellMinSize.setHeight(value);
443 invalidate();
444 }
445 }
446
447
448 /************************************************
449
450 ************************************************/
451 void GridLayout::setCellMinimumWidth(int value)
452 {
453 Q_D(GridLayout);
454 if (d->mPrefCellMinSize.width() != value)
455 {
456 d->mPrefCellMinSize.setWidth(value);
457 invalidate();
458 }
459 }
460
461
462 /************************************************
463
464 ************************************************/
465 QSize GridLayout::cellMaximumSize() const
466 {
467 Q_D(const GridLayout);
468 return d->mPrefCellMaxSize;
469 }
470
471
472 /************************************************
473
474 ************************************************/
475 void GridLayout::setCellMaximumSize(QSize maxSize)
476 {
477 Q_D(GridLayout);
478 if (d->mPrefCellMaxSize != maxSize)
479 {
480 d->mPrefCellMaxSize = maxSize;
481 invalidate();
482 }
483 }
484
485
486 /************************************************
487
488 ************************************************/
489 void GridLayout::setCellMaximumHeight(int value)
490 {
491 Q_D(GridLayout);
492 if (d->mPrefCellMaxSize.height() != value)
493 {
494 d->mPrefCellMaxSize.setHeight(value);
495 invalidate();
496 }
497 }
498
499
500 /************************************************
501
502 ************************************************/
503 void GridLayout::setCellMaximumWidth(int value)
504 {
505 Q_D(GridLayout);
506 if (d->mPrefCellMaxSize.width() != value)
507 {
508 d->mPrefCellMaxSize.setWidth(value);
509 invalidate();
510 }
511 }
512
513
514 /************************************************
515
516 ************************************************/
517 void GridLayout::setCellFixedSize(QSize size)
518 {
519 Q_D(GridLayout);
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 ************************************************/
533 void GridLayout::setCellFixedHeight(int value)
534 {
535 Q_D(GridLayout);
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 ************************************************/
549 void GridLayout::setCellFixedWidth(int value)
550 {
551 Q_D(GridLayout);
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 ************************************************/
565 QSize GridLayout::sizeHint() const
566 {
567 Q_D(const GridLayout);
568
569 if (!d->mIsValid)
570 const_cast<GridLayoutPrivate*>(d)->updateCache();
571
572 return QSize(d->cols() * d->mCellSizeHint.width(),
573 d->rows() * d->mCellSizeHint.height());
574 }
575
576
577 /************************************************
578
579 ************************************************/
580 void GridLayout::setGeometry(const QRect &geometry)
581 {
582 Q_D(GridLayout);
583
584 const bool visual_h_reversed = parentWidget() && parentWidget()->isRightToLeft();
585
586 QLayout::setGeometry(geometry);
587 const QPoint occupied_start = visual_h_reversed ? geometry.topRight() : geometry.topLeft();
588 d->mOccupiedGeometry.setTopLeft(occupied_start);
589 d->mOccupiedGeometry.setBottomRight(occupied_start);
590
591 if (!d->mIsValid)
592 d->updateCache();
593
594 int y = geometry.top();
595 int x = geometry.left();
596
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
603 const int maxX = geometry.left() + geometry.width();
604 const int maxY = geometry.top() + geometry.height();
605
606 const bool stretch_h = d->mStretch.testFlag(StretchHorizontal);
607 const bool stretch_v = d->mStretch.testFlag(StretchVertical);
608
609 const int cols = d->cols();
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;
617
618 const int rows = d->rows();
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;
626
627 #if 0
628 qDebug() << "** GridLayout::setGeometry *******************************";
629 qDebug() << "Geometry:" << geometry;
630 qDebug() << "CellSize:" << d->mCellSizeHint;
631 qDebug() << "Constraints:" << "min" << d->mPrefCellMinSize << "max" << d->mPrefCellMaxSize;
632 qDebug() << "Count" << count();
633 qDebug() << "Cols:" << d->cols() << "(" << d->mColumnCount << ")";
634 qDebug() << "Rows:" << d->rows() << "(" << d->mRowCount << ")";
635 qDebug() << "Stretch:" << "h:" << (d->mStretch.testFlag(StretchHorizontal)) << " v:" << (d->mStretch.testFlag(StretchVertical));
636 qDebug() << "Item:" << "h:" << itemHeight << " w:" << itemWidth;
637 #endif
638
639 int remain_height = heightRemain;
640 int remain_width = widthRemain;
641 if (d->mDirection == LeftToRight)
642 {
643 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
644 foreach(QLayoutItem *item, d->mItems)
645 {
646 if (!item->widget() || item->widget()->isHidden())
647 continue;
648 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
649
650 if (x + width > maxX)
651 {
652 x = geometry.left();
653 y += height;
654
655 height = itemHeight + (0 < remain_height-- ? 1 : 0);
656 remain_width = widthRemain;
657 }
658
659 const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x;
660 d->setItemGeometry(item, QRect(left, y, width, height));
661 x += width;
662 }
663 }
664 else
665 {
666 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
667 foreach(QLayoutItem *item, d->mItems)
668 {
669 if (!item->widget() || item->widget()->isHidden())
670 continue;
671 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
672
673 if (y + height > maxY)
674 {
675 y = geometry.top();
676 x += width;
677
678 width = itemWidth + (0 < remain_width-- ? 1 : 0);
679 remain_height = heightRemain;
680 }
681 const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x;
682 d->setItemGeometry(item, QRect(left, y, width, height));
683 y += height;
684 }
685 }
686 d->mAnimate = false;
687 }
688
689 /************************************************
690
691 ************************************************/
692 QRect GridLayout::occupiedGeometry() const
693 {
694 return d_func()->mOccupiedGeometry;
695 }