You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
668 lines
17 KiB
668 lines
17 KiB
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
|
* (c)LGPL2+
|
|
*
|
|
* LXQt - a lightweight, Qt based, desktop toolset
|
|
* http://razor-qt.org
|
|
*
|
|
* Copyright: 2012 Razor team
|
|
* Authors:
|
|
* Alexander Sokoloff <sokoloff.a@gmail.com>
|
|
*
|
|
* 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 <QDebug>
|
|
#include <math.h>
|
|
#include <QWidget>
|
|
#include <QVariantAnimation>
|
|
|
|
using namespace LXQt;
|
|
|
|
namespace
|
|
{
|
|
class ItemMoveAnimation : public QVariantAnimation
|
|
{
|
|
public:
|
|
static void animate(QLayoutItem * item, QRect const & geometry)
|
|
{
|
|
ItemMoveAnimation* animation = new ItemMoveAnimation(item);
|
|
animation->setStartValue(item->geometry());
|
|
animation->setEndValue(geometry);
|
|
animation->start(DeleteWhenStopped);
|
|
}
|
|
|
|
ItemMoveAnimation(QLayoutItem *item)
|
|
: mItem(item)
|
|
{
|
|
setEasingCurve(QEasingCurve::OutBack);
|
|
setDuration(250);
|
|
}
|
|
|
|
void updateCurrentValue(const QVariant ¤t)
|
|
{
|
|
mItem->setGeometry(current.toRect());
|
|
}
|
|
|
|
private:
|
|
QLayoutItem* mItem;
|
|
|
|
};
|
|
}
|
|
|
|
class LXQt::GridLayoutPrivate
|
|
{
|
|
public:
|
|
GridLayoutPrivate();
|
|
|
|
QList<QLayoutItem*> mItems;
|
|
int mRowCount;
|
|
int mColumnCount;
|
|
GridLayout::Direction mDirection;
|
|
|
|
bool mIsValid;
|
|
QSize mCellSizeHint;
|
|
QSize mCellMaxSize;
|
|
int mVisibleCount;
|
|
GridLayout::Stretch mStretch;
|
|
bool mAnimate;
|
|
|
|
|
|
void updateCache();
|
|
int rows() const;
|
|
int cols() const;
|
|
void setItemGeometry(QLayoutItem * item, QRect const & geometry);
|
|
QSize mPrefCellMinSize;
|
|
QSize mPrefCellMaxSize;
|
|
QRect mOccupiedGeometry;
|
|
};
|
|
|
|
|
|
/************************************************
|
|
|
|
************************************************/
|
|
GridLayoutPrivate::GridLayoutPrivate()
|
|
{
|
|
mColumnCount = 0;
|
|
mRowCount = 0;
|
|
mDirection = GridLayout::LeftToRight;
|
|
mIsValid = false;
|
|
mVisibleCount = 0;
|
|
mStretch = GridLayout::StretchHorizontal | GridLayout::StretchVertical;
|
|
mAnimate = false;
|
|
mPrefCellMinSize = QSize(0,0);
|
|
mPrefCellMaxSize = QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
|
}
|
|
|
|
|
|
/************************************************
|
|
|
|
************************************************/
|
|
void GridLayoutPrivate::updateCache()
|
|
{
|
|
mCellSizeHint = QSize(0, 0);
|
|
mCellMaxSize = QSize(0, 0);
|
|
mVisibleCount = 0;
|
|
|
|
for (int i=0; i<mItems.count(); ++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);
|
|
} 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();
|
|
}
|
|
|
|
|
|
/************************************************
|
|
|
|
************************************************/
|
|
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<GridLayoutPrivate*>(d)->updateCache();
|
|
|
|
return QSize(d->cols() * d->mCellSizeHint.width(),
|
|
d->rows() * d->mCellSizeHint.height());
|
|
}
|
|
|
|
|
|
/************************************************
|
|
|
|
************************************************/
|
|
void GridLayout::setGeometry(const QRect &geometry)
|
|
{
|
|
Q_D(GridLayout);
|
|
|
|
QLayout::setGeometry(geometry);
|
|
d->mOccupiedGeometry.setTopLeft(geometry.topLeft());
|
|
d->mOccupiedGeometry.setBottomRight(geometry.topLeft());
|
|
|
|
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;
|
|
}
|
|
|
|
d->setItemGeometry(item, QRect(x, 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;
|
|
}
|
|
d->setItemGeometry(item, QRect(x, y, width, height));
|
|
y += height;
|
|
}
|
|
}
|
|
d->mAnimate = false;
|
|
}
|
|
|
|
/************************************************
|
|
|
|
************************************************/
|
|
QRect GridLayout::occupiedGeometry() const
|
|
{
|
|
return d_func()->mOccupiedGeometry;
|
|
}
|