File indexing completed on 2024-04-28 03:59:02

0001 /*
0002     This file is part of the KDE frameworks
0003     SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 #include <kcolumnresizer.h>
0008 
0009 #include "loggingcategory.h"
0010 
0011 #include <QEvent>
0012 #include <QGridLayout>
0013 #include <QSet>
0014 #include <QTimer>
0015 #include <QWidget>
0016 
0017 class FormLayoutWidgetItem : public QWidgetItem
0018 {
0019 public:
0020     FormLayoutWidgetItem(QWidget *widget, QFormLayout *formLayout, QFormLayout::ItemRole itemRole)
0021         : QWidgetItem(widget)
0022         , m_formLayout(formLayout)
0023         , m_itemRole(itemRole)
0024     {
0025     }
0026 
0027     void setWidth(int width)
0028     {
0029         if (width != m_width) {
0030             m_width = width;
0031             invalidate();
0032         }
0033     }
0034 
0035     QFormLayout *formLayout() const
0036     {
0037         return m_formLayout;
0038     }
0039 
0040     QSize sizeHint() const override
0041     {
0042         QSize size = QWidgetItem::sizeHint();
0043         if (m_width != -1) {
0044             size.setWidth(m_width);
0045         }
0046         return size;
0047     }
0048 
0049     QSize minimumSize() const override
0050     {
0051         QSize size = QWidgetItem::minimumSize();
0052         if (m_width != -1) {
0053             size.setWidth(m_width);
0054         }
0055         return size;
0056     }
0057 
0058     QSize maximumSize() const override
0059     {
0060         QSize size = QWidgetItem::maximumSize();
0061         if (m_width != -1) {
0062             size.setWidth(m_width);
0063         }
0064         return size;
0065     }
0066 
0067     void setGeometry(const QRect &_rect) override
0068     {
0069         QRect rect = _rect;
0070         int width = widget()->sizeHint().width();
0071         if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
0072             rect.setLeft(rect.right() - width);
0073         }
0074         QWidgetItem::setGeometry(rect);
0075     }
0076 
0077 private:
0078     QFormLayout *const m_formLayout;
0079     int m_width = -1;
0080     const QFormLayout::ItemRole m_itemRole;
0081 };
0082 
0083 struct GridColumnInfo {
0084     GridColumnInfo(QGridLayout *layout_, int column_)
0085         : layout(layout_)
0086         , column(column_)
0087     {
0088     }
0089     QGridLayout *layout;
0090     int column;
0091 };
0092 
0093 Q_DECLARE_TYPEINFO(GridColumnInfo, Q_PRIMITIVE_TYPE);
0094 
0095 class KColumnResizerPrivate
0096 {
0097 public:
0098     KColumnResizerPrivate(KColumnResizer *q_ptr)
0099         : q(q_ptr)
0100         , m_updateTimer(new QTimer(q))
0101     {
0102         m_updateTimer->setSingleShot(true);
0103         m_updateTimer->setInterval(0);
0104         QObject::connect(m_updateTimer, &QTimer::timeout, q, [this]() {
0105             updateWidth();
0106         });
0107     }
0108 
0109     void scheduleWidthUpdate()
0110     {
0111         m_updateTimer->start();
0112     }
0113 
0114     void updateWidth()
0115     {
0116         int width = 0;
0117         for (QWidget *widget : std::as_const(m_widgets)) {
0118             width = qMax(widget->sizeHint().width(), width);
0119         }
0120         for (FormLayoutWidgetItem *item : std::as_const(m_formWidgetItemList)) {
0121             item->setWidth(width);
0122             item->formLayout()->update();
0123         }
0124         for (const GridColumnInfo &info : std::as_const(m_gridColumnInfoList)) {
0125             info.layout->setColumnMinimumWidth(info.column, width);
0126         }
0127     }
0128 
0129     void addWidgetsFromGridLayout(QGridLayout *layout, int column)
0130     {
0131         for (int row = 0; row < layout->rowCount(); ++row) {
0132             QLayoutItem *item = layout->itemAtPosition(row, column);
0133             if (!item) {
0134                 continue;
0135             }
0136             QWidget *widget = item->widget();
0137             if (!widget) {
0138                 continue;
0139             }
0140             q->addWidget(widget);
0141         }
0142         m_gridColumnInfoList << GridColumnInfo(layout, column);
0143     }
0144 
0145     void addWidgetsFromFormLayout(QFormLayout *layout, QFormLayout::ItemRole role)
0146     {
0147         for (int row = 0; row < layout->rowCount(); ++row) {
0148             QLayoutItem *item = layout->itemAt(row, role);
0149             if (!item) {
0150                 continue;
0151             }
0152             QWidget *widget = item->widget();
0153             if (!widget) {
0154                 continue;
0155             }
0156             // Replace the QWidgetItem with our own
0157             layout->removeItem(item);
0158             delete item;
0159             FormLayoutWidgetItem *newItem = new FormLayoutWidgetItem(widget, layout, role);
0160             layout->setItem(row, role, newItem);
0161             m_formWidgetItemList << newItem;
0162 
0163             q->addWidget(widget);
0164         }
0165     }
0166 
0167     KColumnResizer *q;
0168     QTimer *m_updateTimer;
0169     QSet<QWidget *> m_widgets;
0170     QList<FormLayoutWidgetItem *> m_formWidgetItemList;
0171     QList<GridColumnInfo> m_gridColumnInfoList;
0172 };
0173 
0174 KColumnResizer::KColumnResizer(QObject *parent)
0175     : QObject(parent)
0176     , d(new KColumnResizerPrivate(this))
0177 {
0178 }
0179 
0180 KColumnResizer::~KColumnResizer() = default;
0181 
0182 void KColumnResizer::addWidget(QWidget *widget)
0183 {
0184     if (d->m_widgets.contains(widget)) {
0185         return;
0186     }
0187     d->m_widgets.insert(widget);
0188     widget->installEventFilter(this);
0189     d->scheduleWidthUpdate();
0190 }
0191 
0192 void KColumnResizer::removeWidget(QWidget *widget)
0193 {
0194     if (!d->m_widgets.remove(widget)) {
0195         return;
0196     }
0197     widget->removeEventFilter(this);
0198     d->scheduleWidthUpdate();
0199 }
0200 
0201 bool KColumnResizer::eventFilter(QObject *, QEvent *event)
0202 {
0203     if (event->type() == QEvent::Resize) {
0204         d->scheduleWidthUpdate();
0205     }
0206     return false;
0207 }
0208 
0209 void KColumnResizer::addWidgetsFromLayout(QLayout *layout, int column)
0210 {
0211     Q_ASSERT(column >= 0);
0212     if (column < 0) {
0213         qCWarning(KWidgetsAddonsLog) << "column must be >= 0";
0214         return;
0215     }
0216     QGridLayout *gridLayout = qobject_cast<QGridLayout *>(layout);
0217     if (gridLayout) {
0218         d->addWidgetsFromGridLayout(gridLayout, column);
0219         return;
0220     }
0221     QFormLayout *formLayout = qobject_cast<QFormLayout *>(layout);
0222     if (formLayout) {
0223         Q_ASSERT(column <= QFormLayout::SpanningRole);
0224         if (column > QFormLayout::SpanningRole) {
0225             qCWarning(KWidgetsAddonsLog) << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
0226             return;
0227         }
0228         QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
0229         d->addWidgetsFromFormLayout(formLayout, role);
0230     } else {
0231         qCWarning(KWidgetsAddonsLog) << "Don't know how to handle layout" << layout;
0232         Q_ASSERT(0);
0233     }
0234 }
0235 
0236 #include "moc_kcolumnresizer.cpp"
0237 
0238 // vi: ts=4 sw=4 et