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
17class FormLayoutWidgetItem : public QWidgetItem
18{
19public:
20 FormLayoutWidgetItem(QWidget *widget, QFormLayout *formLayout, QFormLayout::ItemRole itemRole)
21 : QWidgetItem(widget)
22 , m_formLayout(formLayout)
23 , m_itemRole(itemRole)
24 {
25 }
26
27 void setWidth(int width)
28 {
29 if (width != m_width) {
30 m_width = width;
31 invalidate();
32 }
33 }
34
35 QFormLayout *formLayout() const
36 {
37 return m_formLayout;
38 }
39
40 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
49 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
58 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
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
77private:
78 QFormLayout *const m_formLayout;
79 int m_width = -1;
80 const QFormLayout::ItemRole m_itemRole;
81};
82
83struct GridColumnInfo {
84 GridColumnInfo(QGridLayout *layout_, int column_)
85 : layout(layout_)
86 , column(column_)
87 {
88 }
89 QGridLayout *layout;
90 int column;
91};
92
93Q_DECLARE_TYPEINFO(GridColumnInfo, Q_PRIMITIVE_TYPE);
94
95class KColumnResizerPrivate
96{
97public:
98 KColumnResizerPrivate(KColumnResizer *q_ptr)
99 : q(q_ptr)
100 , m_updateTimer(new QTimer(q))
101 {
102 m_updateTimer->setSingleShot(true);
103 m_updateTimer->setInterval(0);
104 QObject::connect(sender: m_updateTimer, signal: &QTimer::timeout, context: q, slot: [this]() {
105 updateWidth();
106 });
107 }
108
109 void scheduleWidthUpdate()
110 {
111 m_updateTimer->start();
112 }
113
114 void updateWidth()
115 {
116 int width = 0;
117 for (QWidget *widget : std::as_const(t&: m_widgets)) {
118 width = qMax(a: widget->sizeHint().width(), b: width);
119 }
120 for (FormLayoutWidgetItem *item : std::as_const(t&: m_formWidgetItemList)) {
121 item->setWidth(width);
122 item->formLayout()->update();
123 }
124 for (const GridColumnInfo &info : std::as_const(t&: m_gridColumnInfoList)) {
125 info.layout->setColumnMinimumWidth(column: info.column, minSize: width);
126 }
127 }
128
129 void addWidgetsFromGridLayout(QGridLayout *layout, int column)
130 {
131 for (int row = 0; row < layout->rowCount(); ++row) {
132 QLayoutItem *item = layout->itemAtPosition(row, column);
133 if (!item) {
134 continue;
135 }
136 QWidget *widget = item->widget();
137 if (!widget) {
138 continue;
139 }
140 q->addWidget(widget);
141 }
142 m_gridColumnInfoList << GridColumnInfo(layout, column);
143 }
144
145 void addWidgetsFromFormLayout(QFormLayout *layout, QFormLayout::ItemRole role)
146 {
147 for (int row = 0; row < layout->rowCount(); ++row) {
148 QLayoutItem *item = layout->itemAt(row, role);
149 if (!item) {
150 continue;
151 }
152 QWidget *widget = item->widget();
153 if (!widget) {
154 continue;
155 }
156 // Replace the QWidgetItem with our own
157 layout->removeItem(item);
158 delete item;
159 FormLayoutWidgetItem *newItem = new FormLayoutWidgetItem(widget, layout, role);
160 layout->setItem(row, role, item: newItem);
161 m_formWidgetItemList << newItem;
162
163 q->addWidget(widget);
164 }
165 }
166
167 KColumnResizer *q;
168 QTimer *m_updateTimer;
169 QSet<QWidget *> m_widgets;
170 QList<FormLayoutWidgetItem *> m_formWidgetItemList;
171 QList<GridColumnInfo> m_gridColumnInfoList;
172};
173
174KColumnResizer::KColumnResizer(QObject *parent)
175 : QObject(parent)
176 , d(new KColumnResizerPrivate(this))
177{
178}
179
180KColumnResizer::~KColumnResizer() = default;
181
182void KColumnResizer::addWidget(QWidget *widget)
183{
184 if (d->m_widgets.contains(value: widget)) {
185 return;
186 }
187 d->m_widgets.insert(value: widget);
188 widget->installEventFilter(filterObj: this);
189 d->scheduleWidthUpdate();
190}
191
192void KColumnResizer::removeWidget(QWidget *widget)
193{
194 if (!d->m_widgets.remove(value: widget)) {
195 return;
196 }
197 widget->removeEventFilter(obj: this);
198 d->scheduleWidthUpdate();
199}
200
201bool KColumnResizer::eventFilter(QObject *, QEvent *event)
202{
203 if (event->type() == QEvent::Resize) {
204 d->scheduleWidthUpdate();
205 }
206 return false;
207}
208
209void KColumnResizer::addWidgetsFromLayout(QLayout *layout, int column)
210{
211 Q_ASSERT(column >= 0);
212 if (column < 0) {
213 qCWarning(KWidgetsAddonsLog) << "column must be >= 0";
214 return;
215 }
216 QGridLayout *gridLayout = qobject_cast<QGridLayout *>(object: layout);
217 if (gridLayout) {
218 d->addWidgetsFromGridLayout(layout: gridLayout, column);
219 return;
220 }
221 QFormLayout *formLayout = qobject_cast<QFormLayout *>(object: layout);
222 if (formLayout) {
223 Q_ASSERT(column <= QFormLayout::SpanningRole);
224 if (column > QFormLayout::SpanningRole) {
225 qCWarning(KWidgetsAddonsLog) << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
226 return;
227 }
228 QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
229 d->addWidgetsFromFormLayout(layout: formLayout, role);
230 } else {
231 qCWarning(KWidgetsAddonsLog) << "Don't know how to handle layout" << layout;
232 Q_ASSERT(0);
233 }
234}
235
236#include "moc_kcolumnresizer.cpp"
237
238// vi: ts=4 sw=4 et
239

source code of kwidgetsaddons/src/kcolumnresizer.cpp