Marble

PluginItemDelegate.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2009 Bastian Holst <bastianholst@gmx.de>
4//
5
6// Self
7#include "PluginItemDelegate.h"
8
9// Marble
10#include "MarbleDebug.h"
11#include "RenderPluginModel.h"
12
13// Qt
14#include <QAbstractItemView>
15#include <QApplication>
16#include <QEvent>
17#include <QMouseEvent>
18#include <QPainter>
19#include <QSize>
20#include <QStandardItemModel>
21#include <QVariant>
22
23using namespace Marble;
24/* TRANSLATOR Marble::PluginItemDelegate */
25
26const QSize iconSize(16, 16);
27
28PluginItemDelegate::PluginItemDelegate(QAbstractItemView *view, QObject *parent)
29 : QAbstractItemDelegate(parent)
30{
31 // Enable mouse tracking of itemview makes it possible to find when the mouse if moved
32 // without pressed buttons.
33 view->setMouseTracking(true);
34}
35
36PluginItemDelegate::~PluginItemDelegate() = default;
37
38void PluginItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
39{
40 Q_ASSERT(index.isValid());
41 QRect rect = option.rect;
42 QStyle *style = QApplication::style();
43
44 painter->save();
45
46 // Drawing the background
47 QStyleOption background = option;
48 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
49
50 painter->translate(rect.topLeft());
51
52 // rect is now represented in item coordinates
53 rect.moveTopLeft(QPoint(0, 0));
54 // The point at the top left of the available drawing area.
55 QPoint topLeft(0, 0);
56 // The point at the top right of the available drawing area.
57 QPoint topRight(rect.topRight());
58
59 QRect nameRect = rect;
60
61 // Painting the checkbox
62 QStyleOptionButton checkBox = checkboxOption(option, index, topLeft.x(), Qt::AlignLeft);
63 painter->save();
64 style->drawControl(QStyle::CE_CheckBox, &checkBox, painter);
65 painter->restore();
66
67 nameRect.setLeft(checkBox.rect.right() + 1);
68
69 // Painting the About Button
70 QStyleOptionButton button = buttonOption(option, index, PluginItemDelegate::About, topRight.x(), Qt::AlignRight);
71 style->drawControl(QStyle::CE_PushButton, &button, painter);
72 topRight -= QPoint(button.rect.width(), 0);
73
74 // Painting the Configure Button
75 if (index.data(RenderPluginModel::ConfigurationDialogAvailable).toBool()) {
76 QStyleOptionButton button = buttonOption(option, index, PluginItemDelegate::Configure, topRight.x(), Qt::AlignRight);
77 style->drawControl(QStyle::CE_PushButton, &button, painter);
78 topRight -= QPoint(button.rect.width(), 0);
79
80 nameRect.setRight(button.rect.left() - 1);
81 }
82
83 // Painting the Icon
84 const auto icon = index.data(Qt::DecorationRole).value<QIcon>();
85 const QPixmap iconPixmap = icon.pixmap(16, 16);
86
87 nameRect.moveBottom(nameRect.bottom() + 5);
88 style->drawItemPixmap(painter, nameRect, Qt::AlignLeft, iconPixmap);
89
90 nameRect.setLeft(nameRect.left() + 16 + 5);
91 nameRect.moveBottom(nameRect.bottom() - 5);
92
93 // Painting the Name string
95
96 style->drawItemText(painter, nameRect, Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, name);
97
98 painter->restore();
99}
100
101QSize PluginItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
102{
103 QSize size;
104
105 QStyleOptionViewItem opt = option;
106 opt.rect = QRect(0, 0, 0, 0);
107 QList<QSize> elementSize;
108 elementSize.reserve(4);
109 QStyleOptionButton checkBox = checkboxOption(opt, index);
110 elementSize.append(checkBox.rect.size());
111 QStyleOptionButton aboutButton = buttonOption(opt, index, PluginItemDelegate::About);
112 elementSize.append(aboutButton.rect.size());
113 QStyleOptionButton configButton = buttonOption(opt, index, PluginItemDelegate::Configure);
114 elementSize.append(configButton.rect.size());
115 elementSize.append(nameSize(index));
116
117 for (const QSize &buttonSize : elementSize) {
118 if (buttonSize.height() > size.height())
119 size.setHeight(buttonSize.height());
120 size.setWidth(size.width() + buttonSize.width());
121 }
122
123 return size;
124}
125
126void PluginItemDelegate::setAboutIcon(const QIcon &icon)
127{
128 m_aboutIcon = icon;
129}
130
131void PluginItemDelegate::setConfigIcon(const QIcon &icon)
132{
133 m_configIcon = icon;
134}
135
136bool PluginItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
137{
138 Q_ASSERT(event);
139 Q_ASSERT(model);
140
142 || (event->type() == QEvent::MouseMove)) {
143 auto me = static_cast<QMouseEvent *>(event);
144 QPoint mousePosition = me->pos() - option.rect.topLeft();
145
146 if ((event->type() == QEvent::MouseMove) && !(me->buttons() & Qt::LeftButton)) {
147 // If the mouse moves around and no left button is pressed, no pushbutton is pressed
148 // and no other event will be successful.
149 m_aboutPressedIndex = QModelIndex();
150 m_configPressedIndex = QModelIndex();
151 return true;
152 }
153
154 // Handle checkbox
155 QRect checkRect = checkboxOption(option, index, 0, Qt::AlignLeft).rect;
156 if (checkRect.contains(mousePosition) && ((event->type() == QEvent::MouseButtonDblClick) || (event->type() == QEvent::MouseButtonRelease))) {
157 // make sure that the item is checkable
158 Qt::ItemFlags flags = model->flags(index);
159 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) || !(flags & Qt::ItemIsEnabled))
160 return false;
161
162 // make sure that we have a check state
163 QVariant checkValue = index.data(Qt::CheckStateRole);
164 if (!checkValue.isValid())
165 return false;
166
167 // eat the double click events inside the check rect
168 if (event->type() == QEvent::MouseButtonDblClick)
169 return true;
170
171 Qt::CheckState state = (static_cast<Qt::CheckState>(checkValue.toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
172 return model->setData(index, state, Qt::CheckStateRole);
173 }
174
175 if ((event->type() == QEvent::MouseMove) && !(me->buttons() & Qt::LeftButton)) {
176 m_aboutPressedIndex = QModelIndex();
177 m_configPressedIndex = QModelIndex();
178 return true;
179 }
180
181 QPoint topRight = option.rect.topRight();
182
183 // Handle aboutButton
184 {
185 QRect aboutRect = buttonOption(option, index, PluginItemDelegate::About, topRight.x(), Qt::AlignRight).rect;
186 if (aboutRect.contains(mousePosition)) {
187 if (event->type() == QEvent::MouseButtonDblClick)
188 return true;
189 if (event->type() == QEvent::MouseButtonPress) {
190 m_aboutPressedIndex = index;
191 m_configPressedIndex = QModelIndex();
192 return true;
193 }
194 if (event->type() == QEvent::MouseButtonRelease) {
195 m_aboutPressedIndex = QModelIndex();
196 m_configPressedIndex = QModelIndex();
197 Q_EMIT aboutPluginClicked(index);
198 return true;
199 }
200 if (event->type() == QEvent::MouseMove) {
201 if (me->buttons() & Qt::LeftButton) {
202 m_aboutPressedIndex = index;
203 m_configPressedIndex = QModelIndex();
204 return true;
205 } else {
206 m_aboutPressedIndex = QModelIndex();
207 m_configPressedIndex = QModelIndex();
208 return true;
209 }
210 }
211 } else {
212 // If the mouse is on the item and the mouse isn't above the button.
213 // no about button is pressed.
214 m_aboutPressedIndex = QModelIndex();
215 }
216 topRight -= QPoint(aboutRect.width(), 0);
217 }
218
219 // Handle configButton
220 // make sure we have config button
221 if (index.data(RenderPluginModel::ConfigurationDialogAvailable).toBool()) {
222 QRect configRect = buttonOption(option, index, PluginItemDelegate::Configure, topRight.x(), Qt::AlignRight).rect;
223 if (configRect.contains(mousePosition)) {
224 if (event->type() == QEvent::MouseButtonDblClick)
225 return true;
226
227 if (event->type() == QEvent::MouseButtonPress) {
228 m_aboutPressedIndex = QModelIndex();
229 m_configPressedIndex = index;
230 return true;
231 }
232 if (event->type() == QEvent::MouseButtonRelease) {
233 m_aboutPressedIndex = QModelIndex();
234 m_configPressedIndex = QModelIndex();
235 Q_EMIT configPluginClicked(index);
236 return true;
237 }
238 if (event->type() == QEvent::MouseMove) {
239 if (me->buttons() & Qt::LeftButton) {
240 m_aboutPressedIndex = QModelIndex();
241 m_configPressedIndex = index;
242 return true;
243 } else {
244 m_aboutPressedIndex = QModelIndex();
245 m_configPressedIndex = QModelIndex();
246 return true;
247 }
248 }
249 } else {
250 // If the mouse is on the item and the mouse isn't above the button.
251 // no config button is pressed.
252 m_configPressedIndex = QModelIndex();
253 }
254
255 topRight -= QPoint(configRect.width(), 0);
256 } else {
257 // If we don't have an config dialog shown and the mouse is over this item,
258 // no config button is pressed.
259 m_configPressedIndex = QModelIndex();
260 }
261 }
262
263 return false;
264}
265
266QStyleOptionButton PluginItemDelegate::checkboxOption(const QStyleOptionViewItem &option, const QModelIndex &index, int position, Qt::AlignmentFlag alignment)
267{
268 QStyleOptionButton styleOptionButton;
269 if (index.data(Qt::CheckStateRole).toBool())
270 styleOptionButton.state = option.state | QStyle::State_On;
271 else
272 styleOptionButton.state = option.state | QStyle::State_Off;
274 if (size.isEmpty()) {
275 // A checkbox has definitely a size != 0
276 styleOptionButton.rect.setSize(QSize(22, 22));
277 } else {
278 styleOptionButton.rect.setSize(QSize(size.width(), size.height()));
279 }
280 styleOptionButton.rect = alignRect(styleOptionButton.rect, option.rect, position, alignment);
281 return styleOptionButton;
282}
283
284QStyleOptionButton PluginItemDelegate::buttonOption(const QStyleOptionViewItem &option,
285 const QModelIndex &index,
286 PluginItemDelegate::ButtonType type,
287 int position,
288 Qt::AlignmentFlag alignment) const
289{
290 QStyleOptionButton buttonOption;
291 buttonOption.state = option.state;
292 buttonOption.state &= ~QStyle::State_HasFocus;
293
294 buttonOption.rect.setTopLeft(QPoint(0, 0));
295 buttonOption.palette = option.palette;
296 buttonOption.features = QStyleOptionButton::None;
297
298 QSize contentSize;
299 if (type == PluginItemDelegate::About) {
300 if (m_aboutIcon.isNull()) {
301 buttonOption.text = tr("About");
302 contentSize = buttonOption.fontMetrics.size(0, buttonOption.text) + QSize(4, 4);
303 } else {
304 buttonOption.icon = m_aboutIcon;
305 buttonOption.iconSize = iconSize;
306 contentSize = iconSize;
307 }
308
309 if (m_aboutPressedIndex == index) {
310 buttonOption.state |= QStyle::State_Sunken;
311 }
312 } else if (type == PluginItemDelegate::Configure) {
313 if (m_configIcon.isNull()) {
314 buttonOption.text = tr("Configure");
315 contentSize = buttonOption.fontMetrics.size(0, buttonOption.text) + QSize(4, 4);
316 } else {
317 buttonOption.icon = m_configIcon;
318 buttonOption.iconSize = iconSize;
319 contentSize = iconSize;
320 }
321 if (m_configPressedIndex == index) {
322 buttonOption.state |= QStyle::State_Sunken;
323 }
324 }
325
326 QSize buttonSize = QApplication::style()->sizeFromContents(QStyle::CT_PushButton, &buttonOption, contentSize);
327 buttonOption.rect.setSize(buttonSize);
328 buttonOption.rect = alignRect(buttonOption.rect, option.rect, position, alignment);
329 return buttonOption;
330}
331
332QSize PluginItemDelegate::nameSize(const QModelIndex &index)
333{
335 // FIXME: QApplication::fontMetrics() doesn't work for non-application fonts
336 QSize nameSize(QApplication::fontMetrics().size(0, name));
337 return nameSize;
338}
339
340QRect PluginItemDelegate::alignRect(const QRect &object, const QRect &frame, int position, Qt::AlignmentFlag alignment)
341{
342 QRect rect = object;
343
344 rect.setTopLeft(QPoint(0, 0));
345 // Moves the object to the middle of the item.
346 if (rect.height() < frame.height()) {
347 rect.moveTop((frame.height() - rect.height()) / 2);
348 }
349
350 if (alignment & Qt::AlignLeft) {
351 rect.moveLeft(position);
352 } else if (alignment & Qt::AlignRight) {
353 rect.moveRight(position);
354 }
355
356 return rect;
357}
358
359#include "moc_PluginItemDelegate.cpp"
QString name(StandardAction id)
Binds a QML item to a specific geodetic location in screen coordinates.
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
QFontMetrics fontMetrics()
QStyle * style()
MouseButtonRelease
bool isNull() const const
void append(QList< T > &&value)
void reserve(qsizetype size)
QVariant data(int role) const const
bool isValid() const const
Q_EMITQ_EMIT
virtual bool event(QEvent *e)
QString tr(const char *sourceText, const char *disambiguation, int n)
void restore()
void save()
void translate(const QPoint &offset)
int x() const const
int bottom() const const
bool contains(const QPoint &point, bool proper) const const
int height() const const
int left() const const
void moveBottom(int y)
void moveLeft(int x)
void moveRight(int x)
void moveTop(int y)
void moveTopLeft(const QPoint &position)
void setLeft(int x)
void setRight(int x)
void setTopLeft(const QPoint &position)
QPoint topLeft() const const
QPoint topRight() const const
int width() const const
int height() const const
bool isEmpty() const const
void setHeight(int height)
void setWidth(int width)
int width() const const
PE_PanelItemViewItem
virtual void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual void drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const const
virtual void drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole) const const
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const const=0
AlignLeft
CheckState
DecorationRole
typedef ItemFlags
LeftButton
bool isValid() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() const const
T value() const const
void setMouseTracking(bool enable)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.