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: |
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 | |
77 | private: |
78 | QFormLayout *const m_formLayout; |
79 | int m_width = -1; |
80 | const QFormLayout::ItemRole m_itemRole; |
81 | }; |
82 | |
83 | struct GridColumnInfo { |
84 | GridColumnInfo(QGridLayout *layout_, int column_) |
85 | : layout(layout_) |
86 | , column(column_) |
87 | { |
88 | } |
89 | QGridLayout *layout; |
90 | int column; |
91 | }; |
92 | |
93 | Q_DECLARE_TYPEINFO(GridColumnInfo, Q_PRIMITIVE_TYPE); |
94 | |
95 | class KColumnResizerPrivate |
96 | { |
97 | public: |
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 | |
174 | KColumnResizer::KColumnResizer(QObject *parent) |
175 | : QObject(parent) |
176 | , d(new KColumnResizerPrivate(this)) |
177 | { |
178 | } |
179 | |
180 | KColumnResizer::~KColumnResizer() = default; |
181 | |
182 | void 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 | |
192 | void 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 | |
201 | bool KColumnResizer::eventFilter(QObject *, QEvent *event) |
202 | { |
203 | if (event->type() == QEvent::Resize) { |
204 | d->scheduleWidthUpdate(); |
205 | } |
206 | return false; |
207 | } |
208 | |
209 | void 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 | |