Updated Russian translation
[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 namespace
38 {
39 class ItemMoveAnimation : public QVariantAnimation
40 {
41 public:
42 static void animate(QLayoutItem * item, QRect const & geometry)
43 {
44 ItemMoveAnimation* animation = new ItemMoveAnimation(item);
45 animation->setStartValue(item->geometry());
46 animation->setEndValue(geometry);
47 animation->start(DeleteWhenStopped);
48 }
49
50 ItemMoveAnimation(QLayoutItem *item)
51 : mItem(item)
52 {
53 setDuration(150);
54 }
55
56 void updateCurrentValue(const QVariant &current)
57 {
58 mItem->setGeometry(current.toRect());
59 }
60
61 private:
62 QLayoutItem* mItem;
63
64 };
65 }
66
67 class LXQt::GridLayoutPrivate
68 {
69 public:
70 GridLayoutPrivate();
71
72 QList<QLayoutItem*> mItems;
73 int mRowCount;
74 int mColumnCount;
75 GridLayout::Direction mDirection;
76
77 bool mIsValid;
78 QSize mCellSizeHint;
79 QSize mCellMaxSize;
80 int mVisibleCount;
81 GridLayout::Stretch mStretch;
82 bool mAnimate;
83
84
85 void updateCache();
86 int rows() const;
87 int cols() const;
88 void setItemGeometry(QLayoutItem * item, QRect const & geometry);
89 QSize mPrefCellMinSize;
90 QSize mPrefCellMaxSize;
91 QRect mOccupiedGeometry;
92 };
93
94
95 /************************************************
96
97 ************************************************/
98 GridLayoutPrivate::GridLayoutPrivate()
99 {
100 mColumnCount = 0;
101 mRowCount = 0;
102 mDirection = GridLayout::LeftToRight;
103 mIsValid = false;
104 mVisibleCount = 0;
105 mStretch = GridLayout::StretchHorizontal | GridLayout::StretchVertical;
106 mAnimate = false;
107 mPrefCellMinSize = QSize(0,0);
108 mPrefCellMaxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
109 }
110
111
112 /************************************************
113
114 ************************************************/
115 void GridLayoutPrivate::updateCache()
116 {
117 mCellSizeHint = QSize(0, 0);
118 mCellMaxSize = QSize(0, 0);
119 mVisibleCount = 0;
120
121 for (int i=0; i<mItems.count(); ++i)
122 {
123 QLayoutItem *item = mItems.at(i);
124 if (!item->widget() || item->widget()->isHidden())
125 continue;
126
127 int h = qBound(item->minimumSize().height(),
128 item->sizeHint().height(),
129 item->maximumSize().height());
130
131 int w = qBound(item->minimumSize().width(),
132 item->sizeHint().width(),
133 item->maximumSize().width());
134
135 mCellSizeHint.rheight() = qMax(mCellSizeHint.height(), h);
136 mCellSizeHint.rwidth() = qMax(mCellSizeHint.width(), w);
137
138 mCellMaxSize.rheight() = qMax(mCellMaxSize.height(), item->maximumSize().height());
139 mCellMaxSize.rwidth() = qMax(mCellMaxSize.width(), item->maximumSize().width());
140 mVisibleCount++;
141
142 #if 0
143 qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
144 qDebug() << "item.min" << item->minimumSize().width();
145 qDebug() << "item.sz " << item->sizeHint().width();
146 qDebug() << "item.max" << item->maximumSize().width();
147 qDebug() << "w h" << w << h;
148 qDebug() << "wid.sizeHint" << item->widget()->sizeHint();
149 qDebug() << "mCellSizeHint:" << mCellSizeHint;
150 qDebug() << "mCellMaxSize: " << mCellMaxSize;
151 qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
152 #endif
153
154 }
155 mCellSizeHint.rwidth() = qBound(mPrefCellMinSize.width(), mCellSizeHint.width(), mPrefCellMaxSize.width());
156 mCellSizeHint.rheight()= qBound(mPrefCellMinSize.height(), mCellSizeHint.height(), mPrefCellMaxSize.height());
157 mIsValid = !mCellSizeHint.isEmpty();
158 }
159
160
161 /************************************************
162
163 ************************************************/
164 int GridLayoutPrivate::rows() const
165 {
166 if (mRowCount)
167 return mRowCount;
168
169 if (!mColumnCount)
170 return 1;
171
172 return ceil(mVisibleCount * 1.0 / mColumnCount);
173 }
174
175
176 /************************************************
177
178 ************************************************/
179 int GridLayoutPrivate::cols() const
180 {
181 if (mColumnCount)
182 return mColumnCount;
183
184 int rows = mRowCount;
185 if (!rows)
186 rows = 1;
187
188 return ceil(mVisibleCount * 1.0 / rows);
189 }
190
191 void GridLayoutPrivate::setItemGeometry(QLayoutItem * item, QRect const & geometry)
192 {
193 mOccupiedGeometry |= geometry;
194 if (mAnimate)
195 {
196 ItemMoveAnimation::animate(item, geometry);
197 } else
198 {
199 item->setGeometry(geometry);
200 }
201 }
202
203
204 /************************************************
205
206 ************************************************/
207 GridLayout::GridLayout(QWidget *parent):
208 QLayout(parent),
209 d_ptr(new GridLayoutPrivate())
210 {
211 }
212
213
214 /************************************************
215
216 ************************************************/
217 GridLayout::~GridLayout()
218 {
219 delete d_ptr;
220 }
221
222
223 /************************************************
224
225 ************************************************/
226 void GridLayout::addItem(QLayoutItem *item)
227 {
228 d_ptr->mItems.append(item);
229 }
230
231
232 /************************************************
233
234 ************************************************/
235 QLayoutItem *GridLayout::itemAt(int index) const
236 {
237 Q_D(const GridLayout);
238 if (index < 0 || index >= d->mItems.count())
239 return 0;
240
241 return d->mItems.at(index);
242 }
243
244
245 /************************************************
246
247 ************************************************/
248 QLayoutItem *GridLayout::takeAt(int index)
249 {
250 Q_D(GridLayout);
251 if (index < 0 || index >= d->mItems.count())
252 return 0;
253
254 QLayoutItem *item = d->mItems.takeAt(index);
255 return item;
256 }
257
258
259 /************************************************
260
261 ************************************************/
262 int GridLayout::count() const
263 {
264 Q_D(const GridLayout);
265 return d->mItems.count();
266 }
267
268
269 /************************************************
270
271 ************************************************/
272 void GridLayout::invalidate()
273 {
274 Q_D(GridLayout);
275 d->mIsValid = false;
276 QLayout::invalidate();
277 }
278
279
280 /************************************************
281
282 ************************************************/
283 int GridLayout::rowCount() const
284 {
285 Q_D(const GridLayout);
286 return d->mRowCount;
287 }
288
289
290 /************************************************
291
292 ************************************************/
293 void GridLayout::setRowCount(int value)
294 {
295 Q_D(GridLayout);
296 if (d->mRowCount != value)
297 {
298 d->mRowCount = value;
299 invalidate();
300 }
301 }
302
303
304 /************************************************
305
306 ************************************************/
307 int GridLayout::columnCount() const
308 {
309 Q_D(const GridLayout);
310 return d->mColumnCount;
311 }
312
313
314 /************************************************
315
316 ************************************************/
317 void GridLayout::setColumnCount(int value)
318 {
319 Q_D(GridLayout);
320 if (d->mColumnCount != value)
321 {
322 d->mColumnCount = value;
323 invalidate();
324 }
325 }
326
327
328 /************************************************
329
330 ************************************************/
331 GridLayout::Direction GridLayout::direction() const
332 {
333 Q_D(const GridLayout);
334 return d->mDirection;
335 }
336
337
338 /************************************************
339
340 ************************************************/
341 void GridLayout::setDirection(GridLayout::Direction value)
342 {
343 Q_D(GridLayout);
344 if (d->mDirection != value)
345 {
346 d->mDirection = value;
347 invalidate();
348 }
349 }
350
351 /************************************************
352
353 ************************************************/
354 GridLayout::Stretch GridLayout::stretch() const
355 {
356 Q_D(const GridLayout);
357 return d->mStretch;
358 }
359
360 /************************************************
361
362 ************************************************/
363 void GridLayout::setStretch(Stretch value)
364 {
365 Q_D(GridLayout);
366 if (d->mStretch != value)
367 {
368 d->mStretch = value;
369 invalidate();
370 }
371 }
372
373
374 /************************************************
375
376 ************************************************/
377 void GridLayout::moveItem(int from, int to, bool withAnimation /*= false*/)
378 {
379 Q_D(GridLayout);
380 d->mAnimate = withAnimation;
381 d->mItems.move(from, to);
382 invalidate();
383 }
384
385
386 /************************************************
387
388 ************************************************/
389 QSize GridLayout::cellMinimumSize() const
390 {
391 Q_D(const GridLayout);
392 return d->mPrefCellMinSize;
393 }
394
395
396 /************************************************
397
398 ************************************************/
399 void GridLayout::setCellMinimumSize(QSize minSize)
400 {
401 Q_D(GridLayout);
402 if (d->mPrefCellMinSize != minSize)
403 {
404 d->mPrefCellMinSize = minSize;
405 invalidate();
406 }
407 }
408
409
410 /************************************************
411
412 ************************************************/
413 void GridLayout::setCellMinimumHeight(int value)
414 {
415 Q_D(GridLayout);
416 if (d->mPrefCellMinSize.height() != value)
417 {
418 d->mPrefCellMinSize.setHeight(value);
419 invalidate();
420 }
421 }
422
423
424 /************************************************
425
426 ************************************************/
427 void GridLayout::setCellMinimumWidth(int value)
428 {
429 Q_D(GridLayout);
430 if (d->mPrefCellMinSize.width() != value)
431 {
432 d->mPrefCellMinSize.setWidth(value);
433 invalidate();
434 }
435 }
436
437
438 /************************************************
439
440 ************************************************/
441 QSize GridLayout::cellMaximumSize() const
442 {
443 Q_D(const GridLayout);
444 return d->mPrefCellMaxSize;
445 }
446
447
448 /************************************************
449
450 ************************************************/
451 void GridLayout::setCellMaximumSize(QSize maxSize)
452 {
453 Q_D(GridLayout);
454 if (d->mPrefCellMaxSize != maxSize)
455 {
456 d->mPrefCellMaxSize = maxSize;
457 invalidate();
458 }
459 }
460
461
462 /************************************************
463
464 ************************************************/
465 void GridLayout::setCellMaximumHeight(int value)
466 {
467 Q_D(GridLayout);
468 if (d->mPrefCellMaxSize.height() != value)
469 {
470 d->mPrefCellMaxSize.setHeight(value);
471 invalidate();
472 }
473 }
474
475
476 /************************************************
477
478 ************************************************/
479 void GridLayout::setCellMaximumWidth(int value)
480 {
481 Q_D(GridLayout);
482 if (d->mPrefCellMaxSize.width() != value)
483 {
484 d->mPrefCellMaxSize.setWidth(value);
485 invalidate();
486 }
487 }
488
489
490 /************************************************
491
492 ************************************************/
493 void GridLayout::setCellFixedSize(QSize size)
494 {
495 Q_D(GridLayout);
496 if (d->mPrefCellMinSize != size ||
497 d->mPrefCellMaxSize != size)
498 {
499 d->mPrefCellMinSize = size;
500 d->mPrefCellMaxSize = size;
501 invalidate();
502 }
503 }
504
505
506 /************************************************
507
508 ************************************************/
509 void GridLayout::setCellFixedHeight(int value)
510 {
511 Q_D(GridLayout);
512 if (d->mPrefCellMinSize.height() != value ||
513 d->mPrefCellMaxSize.height() != value)
514 {
515 d->mPrefCellMinSize.setHeight(value);
516 d->mPrefCellMaxSize.setHeight(value);
517 invalidate();
518 }
519 }
520
521
522 /************************************************
523
524 ************************************************/
525 void GridLayout::setCellFixedWidth(int value)
526 {
527 Q_D(GridLayout);
528 if (d->mPrefCellMinSize.width() != value ||
529 d->mPrefCellMaxSize.width() != value)
530 {
531 d->mPrefCellMinSize.setWidth(value);
532 d->mPrefCellMaxSize.setWidth(value);
533 invalidate();
534 }
535 }
536
537
538 /************************************************
539
540 ************************************************/
541 QSize GridLayout::sizeHint() const
542 {
543 Q_D(const GridLayout);
544
545 if (!d->mIsValid)
546 const_cast<GridLayoutPrivate*>(d)->updateCache();
547
548 return QSize(d->cols() * d->mCellSizeHint.width(),
549 d->rows() * d->mCellSizeHint.height());
550 }
551
552
553 /************************************************
554
555 ************************************************/
556 void GridLayout::setGeometry(const QRect &geometry)
557 {
558 Q_D(GridLayout);
559
560 QLayout::setGeometry(geometry);
561 d->mOccupiedGeometry.setTopLeft(geometry.topLeft());
562 d->mOccupiedGeometry.setBottomRight(geometry.topLeft());
563
564 if (!d->mIsValid)
565 d->updateCache();
566
567 int y = geometry.top();
568 int x = geometry.left();
569
570 // For historical reasons QRect::right returns left() + width() - 1
571 // and QRect::bottom() returns top() + height() - 1;
572 // So we use left() + height() and top() + height()
573 //
574 // http://qt-project.org/doc/qt-4.8/qrect.html
575
576 const int maxX = geometry.left() + geometry.width();
577 const int maxY = geometry.top() + geometry.height();
578
579 const bool stretch_h = d->mStretch.testFlag(StretchHorizontal);
580 const bool stretch_v = d->mStretch.testFlag(StretchVertical);
581
582 const int cols = d->cols();
583 int itemWidth = 0;
584 if (stretch_h && 0 < cols)
585 itemWidth = qMin(geometry.width() / cols, d->mCellMaxSize.width());
586 else
587 itemWidth = d->mCellSizeHint.width();
588 itemWidth = qBound(qMin(d->mPrefCellMinSize.width(), maxX), itemWidth, d->mPrefCellMaxSize.width());
589 const int widthRemain = stretch_h && 0 < itemWidth ? geometry.width() % itemWidth : 0;
590
591 const int rows = d->rows();
592 int itemHeight = 0;
593 if (stretch_v && 0 < rows)
594 itemHeight = qMin(geometry.height() / rows, d->mCellMaxSize.height());
595 else
596 itemHeight = d->mCellSizeHint.height();
597 itemHeight = qBound(qMin(d->mPrefCellMinSize.height(), maxY), itemHeight, d->mPrefCellMaxSize.height());
598 const int heightRemain = stretch_v && 0 < itemHeight ? geometry.height() % itemHeight : 0;
599
600 #if 0
601 qDebug() << "** GridLayout::setGeometry *******************************";
602 qDebug() << "Geometry:" << geometry;
603 qDebug() << "CellSize:" << d->mCellSizeHint;
604 qDebug() << "Constraints:" << "min" << d->mPrefCellMinSize << "max" << d->mPrefCellMaxSize;
605 qDebug() << "Count" << count();
606 qDebug() << "Cols:" << d->cols() << "(" << d->mColumnCount << ")";
607 qDebug() << "Rows:" << d->rows() << "(" << d->mRowCount << ")";
608 qDebug() << "Stretch:" << "h:" << (d->mStretch.testFlag(StretchHorizontal)) << " v:" << (d->mStretch.testFlag(StretchVertical));
609 qDebug() << "Item:" << "h:" << itemHeight << " w:" << itemWidth;
610 #endif
611
612 int remain_height = heightRemain;
613 int remain_width = widthRemain;
614 if (d->mDirection == LeftToRight)
615 {
616 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
617 foreach(QLayoutItem *item, d->mItems)
618 {
619 if (!item->widget() || item->widget()->isHidden())
620 continue;
621 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
622
623 if (x + width > maxX)
624 {
625 x = geometry.left();
626 y += height;
627
628 height = itemHeight + (0 < remain_height-- ? 1 : 0);
629 remain_width = widthRemain;
630 }
631
632 d->setItemGeometry(item, QRect(x, y, width, height));
633 x += width;
634 }
635 }
636 else
637 {
638 int width = itemWidth + (0 < remain_width-- ? 1 : 0);
639 foreach(QLayoutItem *item, d->mItems)
640 {
641 if (!item->widget() || item->widget()->isHidden())
642 continue;
643 int height = itemHeight + (0 < remain_height-- ? 1 : 0);
644
645 if (y + height > maxY)
646 {
647 y = geometry.top();
648 x += width;
649
650 width = itemWidth + (0 < remain_width-- ? 1 : 0);
651 remain_height = heightRemain;
652 }
653 d->setItemGeometry(item, QRect(x, y, width, height));
654 y += height;
655 }
656 }
657 d->mAnimate = false;
658 }
659
660 /************************************************
661
662 ************************************************/
663 QRect GridLayout::occupiedGeometry() const
664 {
665 return d_func()->mOccupiedGeometry;
666 }