Warning, file /graphics/glaxnimate/src/gui/widgets/flow_layout.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "flow_layout.hpp"
0008 #include <QtMath>
0009 
0010 using namespace glaxnimate::gui;
0011 
0012 
0013 FlowLayout::FlowLayout(int items_per_row, int min_w, int max_w, QWidget* parent)
0014     : QLayout(parent), min_w(min_w), max_w(max_w), items_per_row(items_per_row)
0015 {
0016 }
0017 
0018 FlowLayout::~FlowLayout()
0019 {
0020     for ( auto it : items )
0021         delete it;
0022 }
0023 
0024 void FlowLayout::addItem(QLayoutItem* item)
0025 {
0026     items.push_back(item);
0027 }
0028 
0029 int FlowLayout::count() const
0030 {
0031     return items.size();
0032 }
0033 
0034 bool FlowLayout::valid_index(int index) const
0035 {
0036     return index >= 0 && index < int(items.size());
0037 }
0038 
0039 QLayoutItem * FlowLayout::itemAt(int index) const
0040 {
0041     if ( !valid_index(index) )
0042         return nullptr;
0043     return items[index];
0044 }
0045 
0046 QLayoutItem * FlowLayout::takeAt(int index)
0047 {
0048     if ( valid_index(index) )
0049     {
0050         auto p = items[index];
0051         items.erase(items.begin() + index);
0052         return p;
0053     }
0054 
0055     return nullptr;
0056 }
0057 
0058 Qt::Orientations FlowLayout::expandingDirections() const
0059 {
0060 #ifdef Q_OS_ANDROID
0061     return orient;
0062 #else
0063     return QLayout::expandingDirections();
0064 #endif
0065 }
0066 
0067 
0068 bool FlowLayout::hasHeightForWidth() const
0069 {
0070     return orient == Qt::Horizontal;
0071 }
0072 
0073 int FlowLayout::heightForWidth(int width) const
0074 {
0075     return do_layout(QRect(0, 0, width, 0), true).height();
0076 }
0077 
0078 QSize FlowLayout::do_layout(const QRect& rect, bool test_only) const
0079 {
0080     if ( items.size() == 0 )
0081         return QSize(0, 0);
0082 
0083     int left, top, right, bottom;
0084     getContentsMargins(&left, &top, &right, &bottom);
0085     QRect effective_rect = rect.adjusted(+left, +top, -right, -bottom);
0086 
0087     int vertical_spacing = spacing();
0088     int horizontal_spacing = spacing();
0089 
0090     int ipr = items_per_row;
0091     int iw = (effective_rect.width() - horizontal_spacing * (ipr - 1)) / ipr;
0092     int nrows;
0093     int ih;
0094 
0095     if ( fixed_size.isValid() )
0096     {
0097         iw = fixed_size.width();
0098         ih = fixed_size.height();
0099         if ( orient == Qt::Horizontal )
0100         {
0101             ipr =  qMax(1, (effective_rect.width() + horizontal_spacing) / (iw + horizontal_spacing));
0102             nrows = (items.size() + ipr - 1) / ipr;
0103         }
0104         else
0105         {
0106             nrows = qMax(1, (effective_rect.height() + vertical_spacing) / (ih + vertical_spacing));;
0107             ipr =  (items.size() + nrows - 1) / nrows;
0108         }
0109     }
0110     else
0111     {
0112         if ( iw < min_w )
0113         {
0114             iw = min_w;
0115             ipr = (effective_rect.width() + horizontal_spacing) / (min_w + horizontal_spacing);
0116         }
0117         else if ( iw > max_w )
0118         {
0119             ipr = qMin<int>(
0120                 items.size(),
0121                 qCeil(qreal(effective_rect.width() + horizontal_spacing) / (max_w + horizontal_spacing))
0122             );
0123             iw = qMin(max_w, (effective_rect.width() - horizontal_spacing * (ipr - 1)) / ipr);
0124         }
0125 
0126         if ( ipr == 0 )
0127         {
0128             ipr = items_per_row;
0129             iw = (effective_rect.width() - horizontal_spacing * (ipr - 1)) / ipr;
0130         }
0131 
0132         nrows = (items.size() + ipr - 1) / ipr;
0133         ih = (effective_rect.height() - vertical_spacing * (nrows - 1)) / nrows;
0134         if ( !test_only && ih < iw )
0135         {
0136             iw = qMax(ih, min_w);
0137             ipr = (effective_rect.width() + horizontal_spacing) / (iw + horizontal_spacing);
0138         }
0139     }
0140 
0141     QSize new_contents(
0142         ipr * iw + (ipr-1) * horizontal_spacing,
0143         nrows * ih + (nrows-1) * vertical_spacing
0144     );
0145     if ( test_only )
0146         return new_contents;
0147 
0148     contents = new_contents;
0149 
0150     QSize item_size(iw, ih);
0151 
0152     int x = 0;
0153     int y = 0;
0154     for ( int i = 0; i < int(items.size()); i++ )
0155     {
0156         QLayoutItem *item = items[i];
0157 
0158         if ( !test_only )
0159             item->setGeometry(QRect(effective_rect.topLeft() + QPoint(x, y), item_size));
0160 
0161         if ( orient == Qt::Horizontal )
0162         {
0163             if ( i % ipr == ipr-1 )
0164             {
0165                 x = 0;
0166                 y += item_size.height() + vertical_spacing;
0167             }
0168             else
0169             {
0170                 x += item_size.width() + horizontal_spacing;
0171             }
0172         }
0173         else
0174         {
0175             if ( i % nrows == nrows-1 )
0176             {
0177                 y = 0;
0178                 x += item_size.width() + vertical_spacing;
0179             }
0180             else
0181             {
0182                 y += item_size.height() + horizontal_spacing;
0183             }
0184         }
0185     }
0186 
0187     return contents;
0188 }
0189 
0190 void FlowLayout::setGeometry(const QRect& rect)
0191 {
0192     QLayout::setGeometry(rect);
0193     do_layout(rect, false);
0194 }
0195 
0196 QSize FlowLayout::sizeHint() const
0197 {
0198     if ( contents.isValid() )
0199     {
0200         const QMargins margins = contentsMargins();
0201         return contents + QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
0202     }
0203     return minimumSize();
0204 }
0205 
0206 QSize FlowLayout::minimumSize() const
0207 {
0208     QSize size;
0209     for ( const QLayoutItem *item : items )
0210         size = size.expandedTo(item->minimumSize());
0211 
0212     const QMargins margins = contentsMargins();
0213     size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
0214 
0215     return size;
0216 }
0217 
0218 void FlowLayout::set_fixed_item_size(const QSize& size)
0219 {
0220     fixed_size = size;
0221     invalidate();
0222 }
0223 
0224 void FlowLayout::set_orientation(Qt::Orientation orientation)
0225 {
0226     orient = orientation;
0227     invalidate();
0228 }