/* BEGIN_COMMON_COPYRIGHT_HEADER * (c)LGPL2+ * * LXQt - a lightweight, Qt based, desktop toolset * http://razor-qt.org * * Copyright: 2012 Razor team * Authors: * Alexander Sokoloff * * This program or library is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * END_COMMON_COPYRIGHT_HEADER */ #include "lxqtgridlayout.h" #include #include #include #include using namespace LXQt; class LXQt::GridLayoutPrivate { public: GridLayoutPrivate(); ~GridLayoutPrivate(); QList mItems; int mRowCount; int mColumnCount; GridLayout::Direction mDirection; bool mIsValid; QSize mCellSizeHint; QSize mCellMaxSize; int mVisibleCount; GridLayout::Stretch mStretch; bool mAnimate; int mAnimatedItems; //!< counter of currently animated items void updateCache(); int rows() const; int cols() const; void setItemGeometry(QLayoutItem * item, QRect const & geometry); QSize mPrefCellMinSize; QSize mPrefCellMaxSize; QRect mOccupiedGeometry; }; namespace { class ItemMoveAnimation : public QVariantAnimation { public: static void animate(QLayoutItem * item, QRect const & geometry, LXQt::GridLayoutPrivate * layout) { ItemMoveAnimation* animation = new ItemMoveAnimation(item); animation->setStartValue(item->geometry()); animation->setEndValue(geometry); ++layout->mAnimatedItems; connect(animation, &QAbstractAnimation::finished, [layout] { --layout->mAnimatedItems; Q_ASSERT(0 <= layout->mAnimatedItems); }); animation->start(DeleteWhenStopped); } ItemMoveAnimation(QLayoutItem *item) : mItem(item) { setDuration(150); } void updateCurrentValue(const QVariant ¤t) { mItem->setGeometry(current.toRect()); } private: QLayoutItem* mItem; }; } /************************************************ ************************************************/ GridLayoutPrivate::GridLayoutPrivate() { mColumnCount = 0; mRowCount = 0; mDirection = GridLayout::LeftToRight; mIsValid = false; mVisibleCount = 0; mStretch = GridLayout::StretchHorizontal | GridLayout::StretchVertical; mAnimate = false; mAnimatedItems = 0; mPrefCellMinSize = QSize(0,0); mPrefCellMaxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); } /************************************************ ************************************************/ GridLayoutPrivate::~GridLayoutPrivate() { qDeleteAll(mItems); } /************************************************ ************************************************/ void GridLayoutPrivate::updateCache() { mCellSizeHint = QSize(0, 0); mCellMaxSize = QSize(0, 0); mVisibleCount = 0; const int N = mItems.count(); for (int i=0; i < N; ++i) { QLayoutItem *item = mItems.at(i); if (!item->widget() || item->widget()->isHidden()) continue; int h = qBound(item->minimumSize().height(), item->sizeHint().height(), item->maximumSize().height()); int w = qBound(item->minimumSize().width(), item->sizeHint().width(), item->maximumSize().width()); mCellSizeHint.rheight() = qMax(mCellSizeHint.height(), h); mCellSizeHint.rwidth() = qMax(mCellSizeHint.width(), w); mCellMaxSize.rheight() = qMax(mCellMaxSize.height(), item->maximumSize().height()); mCellMaxSize.rwidth() = qMax(mCellMaxSize.width(), item->maximumSize().width()); mVisibleCount++; #if 0 qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"; qDebug() << "item.min" << item->minimumSize().width(); qDebug() << "item.sz " << item->sizeHint().width(); qDebug() << "item.max" << item->maximumSize().width(); qDebug() << "w h" << w << h; qDebug() << "wid.sizeHint" << item->widget()->sizeHint(); qDebug() << "mCellSizeHint:" << mCellSizeHint; qDebug() << "mCellMaxSize: " << mCellMaxSize; qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"; #endif } mCellSizeHint.rwidth() = qBound(mPrefCellMinSize.width(), mCellSizeHint.width(), mPrefCellMaxSize.width()); mCellSizeHint.rheight()= qBound(mPrefCellMinSize.height(), mCellSizeHint.height(), mPrefCellMaxSize.height()); mIsValid = !mCellSizeHint.isEmpty(); } /************************************************ ************************************************/ int GridLayoutPrivate::rows() const { if (mRowCount) return mRowCount; if (!mColumnCount) return 1; return ceil(mVisibleCount * 1.0 / mColumnCount); } /************************************************ ************************************************/ int GridLayoutPrivate::cols() const { if (mColumnCount) return mColumnCount; int rows = mRowCount; if (!rows) rows = 1; return ceil(mVisibleCount * 1.0 / rows); } void GridLayoutPrivate::setItemGeometry(QLayoutItem * item, QRect const & geometry) { mOccupiedGeometry |= geometry; if (mAnimate) { ItemMoveAnimation::animate(item, geometry, this); } else { item->setGeometry(geometry); } } /************************************************ ************************************************/ GridLayout::GridLayout(QWidget *parent): QLayout(parent), d_ptr(new GridLayoutPrivate()) { } /************************************************ ************************************************/ GridLayout::~GridLayout() { delete d_ptr; } /************************************************ ************************************************/ void GridLayout::addItem(QLayoutItem *item) { d_ptr->mItems.append(item); } /************************************************ ************************************************/ QLayoutItem *GridLayout::itemAt(int index) const { Q_D(const GridLayout); if (index < 0 || index >= d->mItems.count()) return 0; return d->mItems.at(index); } /************************************************ ************************************************/ QLayoutItem *GridLayout::takeAt(int index) { Q_D(GridLayout); if (index < 0 || index >= d->mItems.count()) return 0; QLayoutItem *item = d->mItems.takeAt(index); return item; } /************************************************ ************************************************/ int GridLayout::count() const { Q_D(const GridLayout); return d->mItems.count(); } /************************************************ ************************************************/ void GridLayout::invalidate() { Q_D(GridLayout); d->mIsValid = false; QLayout::invalidate(); } /************************************************ ************************************************/ int GridLayout::rowCount() const { Q_D(const GridLayout); return d->mRowCount; } /************************************************ ************************************************/ void GridLayout::setRowCount(int value) { Q_D(GridLayout); if (d->mRowCount != value) { d->mRowCount = value; invalidate(); } } /************************************************ ************************************************/ int GridLayout::columnCount() const { Q_D(const GridLayout); return d->mColumnCount; } /************************************************ ************************************************/ void GridLayout::setColumnCount(int value) { Q_D(GridLayout); if (d->mColumnCount != value) { d->mColumnCount = value; invalidate(); } } /************************************************ ************************************************/ GridLayout::Direction GridLayout::direction() const { Q_D(const GridLayout); return d->mDirection; } /************************************************ ************************************************/ void GridLayout::setDirection(GridLayout::Direction value) { Q_D(GridLayout); if (d->mDirection != value) { d->mDirection = value; invalidate(); } } /************************************************ ************************************************/ GridLayout::Stretch GridLayout::stretch() const { Q_D(const GridLayout); return d->mStretch; } /************************************************ ************************************************/ void GridLayout::setStretch(Stretch value) { Q_D(GridLayout); if (d->mStretch != value) { d->mStretch = value; invalidate(); } } /************************************************ ************************************************/ void GridLayout::moveItem(int from, int to, bool withAnimation /*= false*/) { Q_D(GridLayout); d->mAnimate = withAnimation; d->mItems.move(from, to); invalidate(); } /************************************************ ************************************************/ bool GridLayout::animatedMoveInProgress() const { Q_D(const GridLayout); return 0 < d->mAnimatedItems; } /************************************************ ************************************************/ QSize GridLayout::cellMinimumSize() const { Q_D(const GridLayout); return d->mPrefCellMinSize; } /************************************************ ************************************************/ void GridLayout::setCellMinimumSize(QSize minSize) { Q_D(GridLayout); if (d->mPrefCellMinSize != minSize) { d->mPrefCellMinSize = minSize; invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellMinimumHeight(int value) { Q_D(GridLayout); if (d->mPrefCellMinSize.height() != value) { d->mPrefCellMinSize.setHeight(value); invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellMinimumWidth(int value) { Q_D(GridLayout); if (d->mPrefCellMinSize.width() != value) { d->mPrefCellMinSize.setWidth(value); invalidate(); } } /************************************************ ************************************************/ QSize GridLayout::cellMaximumSize() const { Q_D(const GridLayout); return d->mPrefCellMaxSize; } /************************************************ ************************************************/ void GridLayout::setCellMaximumSize(QSize maxSize) { Q_D(GridLayout); if (d->mPrefCellMaxSize != maxSize) { d->mPrefCellMaxSize = maxSize; invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellMaximumHeight(int value) { Q_D(GridLayout); if (d->mPrefCellMaxSize.height() != value) { d->mPrefCellMaxSize.setHeight(value); invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellMaximumWidth(int value) { Q_D(GridLayout); if (d->mPrefCellMaxSize.width() != value) { d->mPrefCellMaxSize.setWidth(value); invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellFixedSize(QSize size) { Q_D(GridLayout); if (d->mPrefCellMinSize != size || d->mPrefCellMaxSize != size) { d->mPrefCellMinSize = size; d->mPrefCellMaxSize = size; invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellFixedHeight(int value) { Q_D(GridLayout); if (d->mPrefCellMinSize.height() != value || d->mPrefCellMaxSize.height() != value) { d->mPrefCellMinSize.setHeight(value); d->mPrefCellMaxSize.setHeight(value); invalidate(); } } /************************************************ ************************************************/ void GridLayout::setCellFixedWidth(int value) { Q_D(GridLayout); if (d->mPrefCellMinSize.width() != value || d->mPrefCellMaxSize.width() != value) { d->mPrefCellMinSize.setWidth(value); d->mPrefCellMaxSize.setWidth(value); invalidate(); } } /************************************************ ************************************************/ QSize GridLayout::sizeHint() const { Q_D(const GridLayout); if (!d->mIsValid) const_cast(d)->updateCache(); return QSize(d->cols() * d->mCellSizeHint.width(), d->rows() * d->mCellSizeHint.height()); } /************************************************ ************************************************/ void GridLayout::setGeometry(const QRect &geometry) { Q_D(GridLayout); const bool visual_h_reversed = parentWidget() && parentWidget()->isRightToLeft(); QLayout::setGeometry(geometry); const QPoint occupied_start = visual_h_reversed ? geometry.topRight() : geometry.topLeft(); d->mOccupiedGeometry.setTopLeft(occupied_start); d->mOccupiedGeometry.setBottomRight(occupied_start); if (!d->mIsValid) d->updateCache(); int y = geometry.top(); int x = geometry.left(); // For historical reasons QRect::right returns left() + width() - 1 // and QRect::bottom() returns top() + height() - 1; // So we use left() + height() and top() + height() // // http://qt-project.org/doc/qt-4.8/qrect.html const int maxX = geometry.left() + geometry.width(); const int maxY = geometry.top() + geometry.height(); const bool stretch_h = d->mStretch.testFlag(StretchHorizontal); const bool stretch_v = d->mStretch.testFlag(StretchVertical); const int cols = d->cols(); int itemWidth = 0; if (stretch_h && 0 < cols) itemWidth = qMin(geometry.width() / cols, d->mCellMaxSize.width()); else itemWidth = d->mCellSizeHint.width(); itemWidth = qBound(qMin(d->mPrefCellMinSize.width(), maxX), itemWidth, d->mPrefCellMaxSize.width()); const int widthRemain = stretch_h && 0 < itemWidth ? geometry.width() % itemWidth : 0; const int rows = d->rows(); int itemHeight = 0; if (stretch_v && 0 < rows) itemHeight = qMin(geometry.height() / rows, d->mCellMaxSize.height()); else itemHeight = d->mCellSizeHint.height(); itemHeight = qBound(qMin(d->mPrefCellMinSize.height(), maxY), itemHeight, d->mPrefCellMaxSize.height()); const int heightRemain = stretch_v && 0 < itemHeight ? geometry.height() % itemHeight : 0; #if 0 qDebug() << "** GridLayout::setGeometry *******************************"; qDebug() << "Geometry:" << geometry; qDebug() << "CellSize:" << d->mCellSizeHint; qDebug() << "Constraints:" << "min" << d->mPrefCellMinSize << "max" << d->mPrefCellMaxSize; qDebug() << "Count" << count(); qDebug() << "Cols:" << d->cols() << "(" << d->mColumnCount << ")"; qDebug() << "Rows:" << d->rows() << "(" << d->mRowCount << ")"; qDebug() << "Stretch:" << "h:" << (d->mStretch.testFlag(StretchHorizontal)) << " v:" << (d->mStretch.testFlag(StretchVertical)); qDebug() << "Item:" << "h:" << itemHeight << " w:" << itemWidth; #endif int remain_height = heightRemain; int remain_width = widthRemain; if (d->mDirection == LeftToRight) { int height = itemHeight + (0 < remain_height-- ? 1 : 0); foreach(QLayoutItem *item, d->mItems) { if (!item->widget() || item->widget()->isHidden()) continue; int width = itemWidth + (0 < remain_width-- ? 1 : 0); if (x + width > maxX) { x = geometry.left(); y += height; height = itemHeight + (0 < remain_height-- ? 1 : 0); remain_width = widthRemain; } const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x; d->setItemGeometry(item, QRect(left, y, width, height)); x += width; } } else { int width = itemWidth + (0 < remain_width-- ? 1 : 0); foreach(QLayoutItem *item, d->mItems) { if (!item->widget() || item->widget()->isHidden()) continue; int height = itemHeight + (0 < remain_height-- ? 1 : 0); if (y + height > maxY) { y = geometry.top(); x += width; width = itemWidth + (0 < remain_width-- ? 1 : 0); remain_height = heightRemain; } const int left = visual_h_reversed ? geometry.left() + geometry.right() - x - width + 1 : x; d->setItemGeometry(item, QRect(left, y, width, height)); y += height; } } d->mAnimate = false; } /************************************************ ************************************************/ QRect GridLayout::occupiedGeometry() const { return d_func()->mOccupiedGeometry; }