123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /*=========================================================================
- Library: CTK
- Copyright (c) Kitware Inc.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.commontk.org/LICENSE
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- =========================================================================*/
- // Qt includes
- #include <QDebug>
- #include <QStyle>
- #include <QWidget>
- // CTK includes
- #include "ctkFlowLayout.h"
- #include "ctkLogger.h"
- // STD includes
- #include <cmath>
- static ctkLogger logger("org.commontk.libs.widgets.ctkFlowLayout");
- //-----------------------------------------------------------------------------
- class ctkFlowLayoutPrivate
- {
- Q_DECLARE_PUBLIC(ctkFlowLayout);
- protected:
- ctkFlowLayout* const q_ptr;
- public:
- ctkFlowLayoutPrivate(ctkFlowLayout& object);
- void init();
- void deleteAll();
- int doLayout(const QRect &rect, bool testOnly) const;
- int smartSpacing(QStyle::PixelMetric pm) const;
- QSize maxSizeHint(int* visibleItemsCount = 0)const;
- QList<QLayoutItem *> ItemList;
- Qt::Orientation Orientation;
- int HorizontalSpacing;
- int VerticalSpacing;
- bool AlignItems;
- Qt::Orientations PreferredDirections;
- };
- // --------------------------------------------------------------------------
- ctkFlowLayoutPrivate::ctkFlowLayoutPrivate(ctkFlowLayout& object)
- :q_ptr(&object)
- {
- this->HorizontalSpacing = -1;
- this->VerticalSpacing = -1;
- this->Orientation = Qt::Horizontal;
- this->PreferredDirections = Qt::Horizontal | Qt::Vertical;
- this->AlignItems = true;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayoutPrivate::init()
- {
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayoutPrivate::deleteAll()
- {
- Q_Q(ctkFlowLayout);
- foreach(QLayoutItem* item, this->ItemList)
- {
- delete item;
- }
- this->ItemList.clear();
- q->invalidate();
- }
- // --------------------------------------------------------------------------
- QSize ctkFlowLayoutPrivate::maxSizeHint(int *visibleItemsCount)const
- {
- if (visibleItemsCount)
- {
- *visibleItemsCount = 0;
- }
- QSize maxItemSize;
- foreach (QLayoutItem* item, this->ItemList)
- {
- QWidget *wid = item->widget();
- if (wid && !wid->isVisibleTo(wid->parentWidget()))
- {// don't take into account hidden items
- continue;
- }
- maxItemSize.rwidth() = qMax(item->sizeHint().width(), maxItemSize.width());
- maxItemSize.rheight() = qMax(item->sizeHint().height(), maxItemSize.height());
- if (visibleItemsCount)
- {
- ++*visibleItemsCount;
- }
- }
- return maxItemSize;
- }
- // --------------------------------------------------------------------------
- int ctkFlowLayoutPrivate::doLayout(const QRect& rect, bool testOnly)const
- {
- Q_Q(const ctkFlowLayout);
- int left, top, right, bottom;
- q->getContentsMargins(&left, &top, &right, &bottom);
- QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
- QPoint pos = QPoint(effectiveRect.x(), effectiveRect.y());
- int length = 0;
- int max = this->Orientation == Qt::Horizontal ?
- effectiveRect.right() + 1 : effectiveRect.bottom() + 1;
- int maxY = top + bottom;
- QSize maxItemSize = this->AlignItems ? this->maxSizeHint() : QSize();
- int spaceX = q->horizontalSpacing();
- int spaceY = q->verticalSpacing();
- int space = this->Orientation == Qt::Horizontal ? spaceX : spaceY;
- foreach (QLayoutItem* item, this->ItemList)
- {
- QWidget *wid = item->widget();
- if (wid && wid->isHidden())
- {
- continue;
- }
- QPoint next = pos;
- QSize itemSize = this->AlignItems ? maxItemSize : item->sizeHint();
- if (this->Orientation == Qt::Horizontal)
- {
- next += QPoint(itemSize.width() + spaceX, 0);
- }
- else
- {
- next += QPoint(0, itemSize.height() + spaceY);
- }
- if (this->Orientation == Qt::Horizontal &&
- (next.x() - space > max) && length > 0)
- {
- pos = QPoint(effectiveRect.x(), pos.y() + length + space);
- next = pos + QPoint(itemSize.width() + space, 0);
- length = 0;
- }
- else if (this->Orientation == Qt::Vertical &&
- (next.y() - space > max) && length > 0)
- {
- pos = QPoint( pos.x() + length + space, effectiveRect.y());
- next = pos + QPoint(0, itemSize.height() + space);
- length = 0;
- }
- if (!testOnly)
- {
- item->setGeometry(QRect(pos, item->sizeHint()));
- }
- maxY = qMax( maxY , pos.y() + item->sizeHint().height() + bottom);
- pos = next;
- length = qMax(length, this->Orientation == Qt::Horizontal ?
- itemSize.height() : itemSize.width());
- }
- return maxY;
- }
- //-----------------------------------------------------------------------------
- int ctkFlowLayoutPrivate::smartSpacing(QStyle::PixelMetric pm) const
- {
- Q_Q(const ctkFlowLayout);
- QObject* parentObject = q->parent();
- if (!parentObject)
- {
- return -1;
- }
- else if (parentObject->isWidgetType())
- {
- QWidget* parentWidget = qobject_cast<QWidget *>(parentObject);
- return parentWidget->style()->pixelMetric(pm, 0, parentWidget);
- }
- else
- {
- return static_cast<QLayout *>(parentObject)->spacing();
- }
- }
- // --------------------------------------------------------------------------
- ctkFlowLayout::ctkFlowLayout(Qt::Orientation orientation, QWidget *parentWidget)
- : Superclass(parentWidget)
- , d_ptr(new ctkFlowLayoutPrivate(*this))
- {
- Q_D(ctkFlowLayout);
- d->init();
- this->setOrientation(orientation);
- }
- // --------------------------------------------------------------------------
- ctkFlowLayout::ctkFlowLayout(QWidget *parentWidget)
- : Superclass(parentWidget)
- , d_ptr(new ctkFlowLayoutPrivate(*this))
- {
- Q_D(ctkFlowLayout);
- d->init();
- }
- // --------------------------------------------------------------------------
- ctkFlowLayout::ctkFlowLayout()
- : d_ptr(new ctkFlowLayoutPrivate(*this))
- {
- Q_D(ctkFlowLayout);
- d->init();
- }
- // --------------------------------------------------------------------------
- ctkFlowLayout::~ctkFlowLayout()
- {
- Q_D(ctkFlowLayout);
- d->deleteAll();
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setOrientation(Qt::Orientation orientation)
- {
- Q_D(ctkFlowLayout);
- d->Orientation = orientation;
- this->invalidate();
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setPreferredExpandingDirections(Qt::Orientations directions)
- {
- Q_D(ctkFlowLayout);
- d->PreferredDirections = directions;
- }
- // --------------------------------------------------------------------------
- Qt::Orientations ctkFlowLayout::preferredExpandingDirections()const
- {
- Q_D(const ctkFlowLayout);
- return d->PreferredDirections;
- }
-
- // --------------------------------------------------------------------------
- Qt::Orientation ctkFlowLayout::orientation() const
- {
- Q_D(const ctkFlowLayout);
- return d->Orientation;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setHorizontalSpacing(int spacing)
- {
- Q_D(ctkFlowLayout);
- d->HorizontalSpacing = spacing;
- this->invalidate();
- }
- // --------------------------------------------------------------------------
- int ctkFlowLayout::horizontalSpacing() const
- {
- Q_D(const ctkFlowLayout);
- if (d->HorizontalSpacing < 0)
- {
- return d->smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
- }
- return d->HorizontalSpacing;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setVerticalSpacing(int spacing)
- {
- Q_D(ctkFlowLayout);
- d->VerticalSpacing = spacing;
- this->invalidate();
- }
- // --------------------------------------------------------------------------
- int ctkFlowLayout::verticalSpacing() const
- {
- Q_D(const ctkFlowLayout);
- if (d->VerticalSpacing < 0)
- {
- return d->smartSpacing(QStyle::PM_LayoutVerticalSpacing);
- }
- return d->VerticalSpacing;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setAlignItems(bool align)
- {
- Q_D(ctkFlowLayout);
- d->AlignItems = align;
- this->invalidate();
- }
- // --------------------------------------------------------------------------
- bool ctkFlowLayout::alignItems() const
- {
- Q_D(const ctkFlowLayout);
- return d->AlignItems;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::addItem(QLayoutItem *item)
- {
- Q_D(ctkFlowLayout);
- d->ItemList << item;
- this->invalidate();
- }
- // --------------------------------------------------------------------------
- Qt::Orientations ctkFlowLayout::expandingDirections() const
- {
- return Qt::Vertical | Qt::Horizontal;
- }
- // --------------------------------------------------------------------------
- bool ctkFlowLayout::hasHeightForWidth() const
- {
- return true;
- }
- // --------------------------------------------------------------------------
- int ctkFlowLayout::heightForWidth(int width) const
- {
- Q_D(const ctkFlowLayout);
- QRect rect(0, 0, width, 0);
- /// here we see the limitations of the vertical layout, it should be
- /// widthForHeight in this case.
- if (d->AlignItems && d->Orientation == Qt::Vertical)
- {
- int itemCount;
- QSize itemSize = d->maxSizeHint(&itemCount);
- QMargins margins = this->contentsMargins();
- int realWidth = width - margins.left() - margins.right();
- int itemCountPerRow = (realWidth + this->horizontalSpacing())
- / (itemSize.width() + this->horizontalSpacing());
- int rowCount = std::ceil( static_cast<float>(itemCount) / itemCountPerRow);
- rect.setHeight(rowCount * itemSize.height() +
- (rowCount -1) * this->verticalSpacing() +
- margins.top() + margins.bottom());
- }
- int height = d->doLayout(rect, true);
- return height;
- }
- // --------------------------------------------------------------------------
- int ctkFlowLayout::count() const
- {
- Q_D(const ctkFlowLayout);
- return d->ItemList.count();
- }
- // --------------------------------------------------------------------------
- QLayoutItem *ctkFlowLayout::itemAt(int index) const
- {
- Q_D(const ctkFlowLayout);
- if (index < 0 || index >= this->count())
- {
- return 0;
- }
- return d->ItemList[index];
- }
- // --------------------------------------------------------------------------
- QSize ctkFlowLayout::minimumSize() const
- {
- Q_D(const ctkFlowLayout);
- QSize size;
- foreach(QLayoutItem* item, d->ItemList)
- {
- QWidget* widget = item->widget();
- if (widget && !widget->isVisibleTo(widget->parentWidget()))
- {
- continue;
- }
- size = size.expandedTo(item->minimumSize());
- }
- int left, top, right, bottom;
- this->getContentsMargins(&left, &top, &right, &bottom);
- size += QSize(left+right, top+bottom);
- return size;
- }
- // --------------------------------------------------------------------------
- void ctkFlowLayout::setGeometry(const QRect &rect)
- {
- Q_D(ctkFlowLayout);
- this->QLayout::setGeometry(rect);
- d->doLayout(rect, false);
- }
- // --------------------------------------------------------------------------
- QSize ctkFlowLayout::sizeHint() const
- {
- Q_D(const ctkFlowLayout);
- QSize size = QSize(0,0);
- int countX = 0;
- int countY = 0;
- QSize maxSizeHint = d->AlignItems ? d->maxSizeHint() : QSize();
- // Add items
- foreach (QLayoutItem* item, d->ItemList)
- {
- QWidget* widget = item->widget();
- if (widget && !widget->isVisibleTo(widget->parentWidget()))
- {
- continue;
- }
- QSize itemSize = d->AlignItems ? maxSizeHint : item->sizeHint();
- Qt::Orientation grow;
- if (d->PreferredDirections & Qt::Horizontal &&
- !(d->PreferredDirections & Qt::Vertical))
- {
- grow = Qt::Horizontal;
- }
- else if (d->PreferredDirections & Qt::Vertical &&
- !(d->PreferredDirections & Qt::Horizontal))
- {
- grow = Qt::Vertical;
- }
- else
- {
- grow = countY >= countX ? Qt::Horizontal : Qt::Vertical;
- }
- if (grow == Qt::Horizontal)
- {
- size.rwidth() += itemSize.width();
- size.rheight() = qMax(itemSize.height(), size.height());
- ++countX;
- }
- else
- {
- size.rwidth() = qMax(itemSize.width(), size.width());
- size.rheight() += itemSize.height();
- ++countY;
- }
- }
- // Add spacing
- size += QSize((countX-1) * this->horizontalSpacing(),
- (countY-1) * this->verticalSpacing());
- // Add margins
- int left, top, right, bottom;
- this->getContentsMargins(&left, &top, &right, &bottom);
- size += QSize(left+right, top+bottom);
- return size;
- }
- // --------------------------------------------------------------------------
- QLayoutItem *ctkFlowLayout::takeAt(int index)
- {
- Q_D(ctkFlowLayout);
- if (index < 0 || index >= this->count())
- {
- return 0;
- }
- QLayoutItem* item = d->ItemList.takeAt(index);
- this->invalidate();
- return item;
- }
|