KWidgetsAddons

kcolumnresizer.cpp
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)
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 {
43 if (m_width != -1) {
44 size.setWidth(m_width);
45 }
46 return size;
47 }
48
49 QSize minimumSize() const override
50 {
52 if (m_width != -1) {
53 size.setWidth(m_width);
54 }
55 return size;
56 }
57
58 QSize maximumSize() const override
59 {
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 }
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(m_updateTimer, &QTimer::timeout, q, [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(m_widgets)) {
118 width = qMax(widget->sizeHint().width(), width);
119 }
120 for (FormLayoutWidgetItem *item : std::as_const(m_formWidgetItemList)) {
121 item->setWidth(width);
122 item->formLayout()->update();
123 }
124 for (const GridColumnInfo &info : std::as_const(m_gridColumnInfoList)) {
125 info.layout->setColumnMinimumWidth(info.column, 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, newItem);
161 m_formWidgetItemList << newItem;
162
163 q->addWidget(widget);
164 }
165 }
166
168 QTimer *m_updateTimer;
169 QSet<QWidget *> m_widgets;
170 QList<FormLayoutWidgetItem *> m_formWidgetItemList;
171 QList<GridColumnInfo> m_gridColumnInfoList;
172};
173
175 : QObject(parent)
176 , d(new KColumnResizerPrivate(this))
177{
178}
179
180KColumnResizer::~KColumnResizer() = default;
181
183{
184 if (d->m_widgets.contains(widget)) {
185 return;
186 }
187 d->m_widgets.insert(widget);
188 widget->installEventFilter(this);
189 d->scheduleWidthUpdate();
190}
191
193{
194 if (!d->m_widgets.remove(widget)) {
195 return;
196 }
197 widget->removeEventFilter(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
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 *>(layout);
217 if (gridLayout) {
218 d->addWidgetsFromGridLayout(gridLayout, column);
219 return;
220 }
221 QFormLayout *formLayout = qobject_cast<QFormLayout *>(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(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
Maintains consistent column sizes across layouts.
void addWidgetsFromLayout(QLayout *layout, int column=0)
Add all widgets from layout which are in column column to the list of widgets to manage.
void removeWidget(QWidget *widget)
Remove a widget previously added by addWidget or addWidgetsFromLayout.
KColumnResizer(QObject *parent=nullptr)
Constructs a KColumnResizer.
void addWidget(QWidget *widget)
Add a single widget to the list of widgets whose width is monitored.
virtual QLayoutItem * itemAt(int index) const const override
int rowCount() const const
void setItem(int row, ItemRole role, QLayoutItem *item)
QLayoutItem * itemAtPosition(int row, int column) const const
int rowCount() const const
void removeItem(QLayoutItem *item)
virtual void invalidate()
virtual QWidget * widget() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
void installEventFilter(QObject *filterObj)
T qobject_cast(QObject *object)
void removeEventFilter(QObject *obj)
int right() const const
void setLeft(int x)
void setWidth(int width)
AlignRight
void setInterval(int msec)
void setSingleShot(bool singleShot)
void start()
void timeout()
virtual QSize maximumSize() const const override
virtual QSize minimumSize() const const override
virtual void setGeometry(const QRect &rect) override
virtual QSize sizeHint() const const override
virtual QWidget * widget() const const override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:03:10 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.