1 /*
2 This file is part of the KDE frameworks
3 SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 #include <kcolumnresizer.h>
8
9 #include "loggingcategory.h"
10
11 #include <QEvent>
12 #include <QGridLayout>
13 #include <QSet>
14 #include <QTimer>
15 #include <QWidget>
16
17 class FormLayoutWidgetItem : public QWidgetItem
18 {
19 public:
FormLayoutWidgetItem(QWidget * widget,QFormLayout * formLayout,QFormLayout::ItemRole itemRole)20 FormLayoutWidgetItem(QWidget *widget, QFormLayout *formLayout, QFormLayout::ItemRole itemRole)
21 : QWidgetItem(widget)
22 , m_formLayout(formLayout)
23 , m_itemRole(itemRole)
24 {
25 }
26
setWidth(int width)27 void setWidth(int width)
28 {
29 if (width != m_width) {
30 m_width = width;
31 invalidate();
32 }
33 }
34
formLayout() const35 QFormLayout *formLayout() const
36 {
37 return m_formLayout;
38 }
39
sizeHint() const40 QSize sizeHint() const override
41 {
42 QSize size = QWidgetItem::sizeHint();
43 if (m_width != -1) {
44 size.setWidth(m_width);
45 }
46 return size;
47 }
48
minimumSize() const49 QSize minimumSize() const override
50 {
51 QSize size = QWidgetItem::minimumSize();
52 if (m_width != -1) {
53 size.setWidth(m_width);
54 }
55 return size;
56 }
57
maximumSize() const58 QSize maximumSize() const override
59 {
60 QSize size = QWidgetItem::maximumSize();
61 if (m_width != -1) {
62 size.setWidth(m_width);
63 }
64 return size;
65 }
66
setGeometry(const QRect & _rect)67 void setGeometry(const QRect &_rect) override
68 {
69 QRect rect = _rect;
70 int width = widget()->sizeHint().width();
71 if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
72 rect.setLeft(rect.right() - width);
73 }
74 QWidgetItem::setGeometry(rect);
75 }
76
77 private:
78 QFormLayout *const m_formLayout;
79 int m_width = -1;
80 const QFormLayout::ItemRole m_itemRole;
81 };
82
83 struct GridColumnInfo {
GridColumnInfoGridColumnInfo84 GridColumnInfo(QGridLayout *layout_, int column_)
85 : layout(layout_)
86 , column(column_)
87 {
88 }
89 QGridLayout *layout;
90 int column;
91 };
92
93 class KColumnResizerPrivate
94 {
95 public:
KColumnResizerPrivate(KColumnResizer * q_ptr)96 KColumnResizerPrivate(KColumnResizer *q_ptr)
97 : q(q_ptr)
98 , m_updateTimer(new QTimer(q))
99 {
100 m_updateTimer->setSingleShot(true);
101 m_updateTimer->setInterval(0);
102 QObject::connect(m_updateTimer, &QTimer::timeout, q, [this]() {
103 updateWidth();
104 });
105 }
106
scheduleWidthUpdate()107 void scheduleWidthUpdate()
108 {
109 m_updateTimer->start();
110 }
111
updateWidth()112 void updateWidth()
113 {
114 int width = 0;
115 for (QWidget *widget : std::as_const(m_widgets)) {
116 width = qMax(widget->sizeHint().width(), width);
117 }
118 for (FormLayoutWidgetItem *item : std::as_const(m_formWidgetItemList)) {
119 item->setWidth(width);
120 item->formLayout()->update();
121 }
122 for (const GridColumnInfo &info : std::as_const(m_gridColumnInfoList)) {
123 info.layout->setColumnMinimumWidth(info.column, width);
124 }
125 }
126
addWidgetsFromGridLayout(QGridLayout * layout,int column)127 void addWidgetsFromGridLayout(QGridLayout *layout, int column)
128 {
129 for (int row = 0; row < layout->rowCount(); ++row) {
130 QLayoutItem *item = layout->itemAtPosition(row, column);
131 if (!item) {
132 continue;
133 }
134 QWidget *widget = item->widget();
135 if (!widget) {
136 continue;
137 }
138 q->addWidget(widget);
139 }
140 m_gridColumnInfoList << GridColumnInfo(layout, column);
141 }
142
addWidgetsFromFormLayout(QFormLayout * layout,QFormLayout::ItemRole role)143 void addWidgetsFromFormLayout(QFormLayout *layout, QFormLayout::ItemRole role)
144 {
145 for (int row = 0; row < layout->rowCount(); ++row) {
146 QLayoutItem *item = layout->itemAt(row, role);
147 if (!item) {
148 continue;
149 }
150 QWidget *widget = item->widget();
151 if (!widget) {
152 continue;
153 }
154 // Replace the QWidgetItem with our own
155 layout->removeItem(item);
156 delete item;
157 FormLayoutWidgetItem *newItem = new FormLayoutWidgetItem(widget, layout, role);
158 layout->setItem(row, role, newItem);
159 m_formWidgetItemList << newItem;
160
161 q->addWidget(widget);
162 }
163 }
164
165 KColumnResizer *q;
166 QTimer *m_updateTimer;
167 QSet<QWidget *> m_widgets;
168 QList<FormLayoutWidgetItem *> m_formWidgetItemList;
169 QList<GridColumnInfo> m_gridColumnInfoList;
170 };
171
KColumnResizer(QObject * parent)172 KColumnResizer::KColumnResizer(QObject *parent)
173 : QObject(parent)
174 , d(new KColumnResizerPrivate(this))
175 {
176 }
177
178 KColumnResizer::~KColumnResizer() = default;
179
addWidget(QWidget * widget)180 void KColumnResizer::addWidget(QWidget *widget)
181 {
182 if (d->m_widgets.contains(widget)) {
183 return;
184 }
185 d->m_widgets.insert(widget);
186 widget->installEventFilter(this);
187 d->scheduleWidthUpdate();
188 }
189
removeWidget(QWidget * widget)190 void KColumnResizer::removeWidget(QWidget *widget)
191 {
192 if (!d->m_widgets.remove(widget)) {
193 return;
194 }
195 widget->removeEventFilter(this);
196 d->scheduleWidthUpdate();
197 }
198
eventFilter(QObject *,QEvent * event)199 bool KColumnResizer::eventFilter(QObject *, QEvent *event)
200 {
201 if (event->type() == QEvent::Resize) {
202 d->scheduleWidthUpdate();
203 }
204 return false;
205 }
206
addWidgetsFromLayout(QLayout * layout,int column)207 void KColumnResizer::addWidgetsFromLayout(QLayout *layout, int column)
208 {
209 Q_ASSERT(column >= 0);
210 if (column < 0) {
211 qCWarning(KWidgetsAddonsLog) << "column must be >= 0";
212 return;
213 }
214 QGridLayout *gridLayout = qobject_cast<QGridLayout *>(layout);
215 if (gridLayout) {
216 d->addWidgetsFromGridLayout(gridLayout, column);
217 return;
218 }
219 QFormLayout *formLayout = qobject_cast<QFormLayout *>(layout);
220 if (formLayout) {
221 Q_ASSERT(column <= QFormLayout::SpanningRole);
222 if (column > QFormLayout::SpanningRole) {
223 qCWarning(KWidgetsAddonsLog) << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
224 return;
225 }
226 QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
227 d->addWidgetsFromFormLayout(formLayout, role);
228 } else {
229 qCWarning(KWidgetsAddonsLog) << "Don't know how to handle layout" << layout;
230 Q_ASSERT(0);
231 }
232 }
233
234 #include <moc_kcolumnresizer.cpp>
235 // vi: ts=4 sw=4 et
236