KItemViews

kwidgetitemdelegate.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2007-2008 Rafael Fernández López <ereslibre@kde.org>
4 SPDX-FileCopyrightText: 2008 Kevin Ottens <ervin@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kwidgetitemdelegate.h"
10#include "kwidgetitemdelegate_p.h"
11
12#include <QAbstractItemView>
13#include <QApplication>
14#include <QCursor>
15#include <QPainter>
16#include <QStyleOption>
17#include <QTimer>
18#include <QTreeView>
19
20#include "kwidgetitemdelegatepool_p.h"
21
22Q_DECLARE_METATYPE(QList<QEvent::Type>)
23
24/**
25 KWidgetItemDelegatePrivate class that helps to provide binary compatibility between releases.
26 @internal
27*/
28//@cond PRIVATE
29KWidgetItemDelegatePrivate::KWidgetItemDelegatePrivate(KWidgetItemDelegate *q, QObject *parent)
30 : QObject(parent)
31 , widgetPool(new KWidgetItemDelegatePool(q))
32 , q(q)
33{
34}
35
36KWidgetItemDelegatePrivate::~KWidgetItemDelegatePrivate()
37{
38 if (!viewDestroyed) {
39 widgetPool->fullClear();
40 }
41 delete widgetPool;
42}
43
44void KWidgetItemDelegatePrivate::_k_slotRowsInserted(const QModelIndex &parent, int start, int end)
45{
46 Q_UNUSED(end);
47 // We need to update the rows behind the inserted row as well because the widgets need to be
48 // moved to their new position
49 updateRowRange(parent, start, model->rowCount(parent), false);
50}
51
52void KWidgetItemDelegatePrivate::_k_slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
53{
54 updateRowRange(parent, start, end, true);
55}
56
57void KWidgetItemDelegatePrivate::_k_slotRowsRemoved(const QModelIndex &parent, int start, int end)
58{
59 Q_UNUSED(end);
60 // We need to update the rows that come behind the deleted rows because the widgets need to be
61 // moved to the new position
62 updateRowRange(parent, start, model->rowCount(parent), false);
63}
64
65void KWidgetItemDelegatePrivate::_k_slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
66{
67 for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
68 for (int j = topLeft.column(); j <= bottomRight.column(); ++j) {
69 const QModelIndex index = model->index(i, j, topLeft.parent());
70 widgetPool->findWidgets(index, optionView(index));
71 }
72 }
73}
74
75void KWidgetItemDelegatePrivate::_k_slotLayoutChanged()
76{
77 const auto lst = widgetPool->invalidIndexesWidgets();
78 for (QWidget *widget : lst) {
79 widget->setVisible(false);
80 }
81 QTimer::singleShot(0, this, SLOT(initializeModel()));
82}
83
84void KWidgetItemDelegatePrivate::_k_slotModelReset()
85{
86 widgetPool->fullClear();
87 QTimer::singleShot(0, this, SLOT(initializeModel()));
88}
89
90void KWidgetItemDelegatePrivate::_k_slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
91{
92 const auto lstSelected = selected.indexes();
93 for (const QModelIndex &index : lstSelected) {
94 widgetPool->findWidgets(index, optionView(index));
95 }
96 const auto lstDeselected = deselected.indexes();
97 for (const QModelIndex &index : lstDeselected) {
98 widgetPool->findWidgets(index, optionView(index));
99 }
100}
101
102void KWidgetItemDelegatePrivate::updateRowRange(const QModelIndex &parent, int start, int end, bool isRemoving)
103{
104 int i = start;
105 while (i <= end) {
106 for (int j = 0; j < model->columnCount(parent); ++j) {
107 const QModelIndex index = model->index(i, j, parent);
108 const QList<QWidget *> widgetList =
109 widgetPool->findWidgets(index,
110 optionView(index),
111 isRemoving ? KWidgetItemDelegatePool::NotUpdateWidgets : KWidgetItemDelegatePool::UpdateWidgets);
112 if (isRemoving) {
113 for (QWidget *widget : widgetList) {
114 const QModelIndex idx = widgetPool->d->widgetInIndex[widget];
115 widgetPool->d->usedWidgets.remove(idx);
116 widgetPool->d->widgetInIndex.remove(widget);
117 delete widget;
118 }
119 }
120 }
121 i++;
122 }
123}
124
125inline QStyleOptionViewItem KWidgetItemDelegatePrivate::optionView(const QModelIndex &index)
126{
127 QStyleOptionViewItem optionView;
128 optionView.initFrom(itemView->viewport());
129 optionView.rect = itemView->visualRect(index);
130 optionView.decorationSize = itemView->iconSize();
131 return optionView;
132}
133
134void KWidgetItemDelegatePrivate::initializeModel(const QModelIndex &parent)
135{
136 if (!model) {
137 return;
138 }
139
140 for (int i = 0; i < model->rowCount(parent); ++i) {
141 for (int j = 0; j < model->columnCount(parent); ++j) {
142 const QModelIndex index = model->index(i, j, parent);
143 if (index.isValid()) {
144 widgetPool->findWidgets(index, optionView(index));
145 }
146 }
147 // Check if we need to go recursively through the children of parent (if any) to initialize
148 // all possible indexes that are shown.
149 const QModelIndex index = model->index(i, 0, parent);
150 if (index.isValid() && model->hasChildren(index)) {
151 initializeModel(index);
152 }
153 }
154}
155//@endcond
156
158 : QAbstractItemDelegate(parent)
159 , d(new KWidgetItemDelegatePrivate(this))
160{
161 Q_ASSERT(itemView);
162
165
166 d->itemView = itemView;
167
168 itemView->viewport()->installEventFilter(d.get()); // mouse events
169 itemView->installEventFilter(d.get()); // keyboard events
170
172 connect(itemView, SIGNAL(collapsed(QModelIndex)), d.get(), SLOT(initializeModel()));
173 connect(itemView, SIGNAL(expanded(QModelIndex)), d.get(), SLOT(initializeModel()));
174 }
175}
176
178
180{
181 return d->itemView;
182}
183
185{
186 const QPersistentModelIndex idx = d->widgetPool->d->widgetInIndex.value(QApplication::focusWidget());
187 if (idx.isValid()) {
188 return idx;
189 }
190 // Use the mouse position, if the widget refused to take keyboard focus.
191 const QPoint pos = d->itemView->viewport()->mapFromGlobal(QCursor::pos());
192 return d->itemView->indexAt(pos);
193}
194
195//@cond PRIVATE
196bool KWidgetItemDelegatePrivate::eventFilter(QObject *watched, QEvent *event)
197{
198 if (event->type() == QEvent::Destroy) {
199 // we care for the view since it deletes the widgets (parentage).
200 // if the view hasn't been deleted, it might be that just the
201 // delegate is removed from it, in which case we need to remove the widgets
202 // manually, otherwise they still get drawn.
203 if (watched == itemView) {
204 viewDestroyed = true;
205 }
206 return false;
207 }
208
209 Q_ASSERT(itemView);
210
211 // clang-format off
212 if (model != itemView->model()) {
213 if (model) {
214 disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_slotRowsInserted(QModelIndex,int,int)));
215 disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsAboutToBeRemoved(QModelIndex,int,int)));
216 disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsRemoved(QModelIndex,int,int)));
217 disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_slotDataChanged(QModelIndex,QModelIndex)));
218 disconnect(model, SIGNAL(layoutChanged()), q, SLOT(_k_slotLayoutChanged()));
219 disconnect(model, SIGNAL(modelReset()), q, SLOT(_k_slotModelReset()));
220 }
221 model = itemView->model();
222 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(_k_slotRowsInserted(QModelIndex,int,int)));
223 connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsAboutToBeRemoved(QModelIndex,int,int)));
224 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(_k_slotRowsRemoved(QModelIndex,int,int)));
225 connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), q, SLOT(_k_slotDataChanged(QModelIndex,QModelIndex)));
226 connect(model, SIGNAL(layoutChanged()), q, SLOT(_k_slotLayoutChanged()));
227 connect(model, SIGNAL(modelReset()), q, SLOT(_k_slotModelReset()));
228 QTimer::singleShot(0, this, SLOT(initializeModel()));
229 }
230
231 if (selectionModel != itemView->selectionModel()) {
232 if (selectionModel) {
233 disconnect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(_k_slotSelectionChanged(QItemSelection,QItemSelection)));
234 }
235 selectionModel = itemView->selectionModel();
236 connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(_k_slotSelectionChanged(QItemSelection,QItemSelection)));
237 QTimer::singleShot(0, this, SLOT(initializeModel()));
238 }
239 // clang-format on
240
241 switch (event->type()) {
242 case QEvent::Polish:
243 case QEvent::Resize:
244 if (!qobject_cast<QAbstractItemView *>(watched)) {
245 QTimer::singleShot(0, this, SLOT(initializeModel()));
246 }
247 break;
248 case QEvent::FocusIn:
249 case QEvent::FocusOut:
250 if (qobject_cast<QAbstractItemView *>(watched)) {
251 const auto lst = selectionModel->selectedIndexes();
252 for (const QModelIndex &index : lst) {
253 if (index.isValid()) {
254 widgetPool->findWidgets(index, optionView(index));
255 }
256 }
257 }
258 break;
259 default:
260 break;
261 }
262
263 return QObject::eventFilter(watched, event);
264}
265//@endcond
266
268{
269 widget->setProperty("goya:blockedEventTypes", QVariant::fromValue(types));
270}
271
273{
274 return widget->property("goya:blockedEventTypes").value<QList<QEvent::Type>>();
275}
276
278{
279 d->_k_slotModelReset();
280}
281
282#include "moc_kwidgetitemdelegate.cpp"
283#include "moc_kwidgetitemdelegate_p.cpp"
This class allows to create item delegates embedding simple widgets to interact with items.
QAbstractItemView * itemView() const
Retrieves the item view this delegate is monitoring.
~KWidgetItemDelegate() override
Destroys an ItemDelegate.
void setBlockedEventTypes(QWidget *widget, const QList< QEvent::Type > &types) const
Sets the list of event types that a widget will block.
QList< QEvent::Type > blockedEventTypes(QWidget *widget) const
Retrieves the list of blocked event types for the given widget.
void resetModel()
trigger a modelReset
KWidgetItemDelegate(QAbstractItemView *itemView, QObject *parent=nullptr)
Creates a new ItemDelegate to be used with a given itemview.
QPersistentModelIndex focusedIndex() const
Retrieves the currently focused index.
Q_SCRIPTABLE Q_NOREPLY void start()
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QWidget * viewport() const const
QWidget * focusWidget()
QPoint pos()
QModelIndexList indexes() const const
int column() const const
bool isValid() const const
QModelIndex parent() const const
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
QVariant property(const char *name) const const
T qobject_cast(QObject *object)
bool setProperty(const char *name, QVariant &&value)
bool isValid() const const
void initFrom(const QWidget *widget)
WA_Hover
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
T value() const const
void setMouseTracking(bool enable)
void setAttribute(Qt::WidgetAttribute attribute, bool on)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:14:47 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.