Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qoptionswidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtWidgets/qitemdelegate.h>
8#include <QtWidgets/qlayout.h>
9#include <QtWidgets/qlistwidget.h>
10
12
13using namespace Qt::StringLiterals;
14
16{
17public:
19
20 static bool isSeparator(const QModelIndex &index) {
21 return index.data(Qt::AccessibleDescriptionRole).toString() == "separator"_L1;
22 }
23 static void setSeparator(QListWidgetItem *item) {
24 item->setData(Qt::AccessibleDescriptionRole, QString::fromLatin1("separator"));
25 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
26 }
27
28protected:
29 void paint(QPainter *painter, const QStyleOptionViewItem &option,
30 const QModelIndex &index) const override
31 {
32 if (isSeparator(index)) {
33 QRect rect = option.rect;
34 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget))
35 rect.setWidth(view->viewport()->width());
36 QStyleOption opt;
37 opt.rect = rect;
38 m_widget->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter,
39 m_widget);
40 } else {
41 QItemDelegate::paint(painter, option, index);
42 }
43 }
44
45 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
46 {
47 if (isSeparator(index)) {
48 int pm = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
49 return {pm, pm};
50 }
51 return QItemDelegate::sizeHint(option, index);
52 }
53
54private:
55 QWidget *m_widget;
56};
57
58static QStringList subtract(const QStringList &minuend, const QStringList &subtrahend)
59{
60 QStringList result = minuend;
61 for (const QString &str : subtrahend)
62 result.removeOne(str);
63 return result;
64}
65
66QOptionsWidget::QOptionsWidget(QWidget *parent)
67 : QWidget(parent)
68 , m_noOptionText(tr("No Option"))
69 , m_invalidOptionText(tr("Invalid Option"))
70{
71 m_listWidget = new QListWidget(this);
72 m_listWidget->setItemDelegate(new ListWidgetDelegate(m_listWidget));
73 QVBoxLayout *layout = new QVBoxLayout(this);
74 layout->addWidget(m_listWidget);
75 layout->setContentsMargins({});
76 connect(m_listWidget, &QListWidget::itemChanged, this, &QOptionsWidget::itemChanged);
77}
78
79void QOptionsWidget::setOptions(const QStringList &validOptions, const QStringList &selectedOptions)
80{
81 m_listWidget->clear();
82 m_optionToItem.clear();
83 m_itemToOption.clear();
84
85 m_validOptions = validOptions;
86 m_validOptions.removeDuplicates();
87 std::sort(m_validOptions.begin(), m_validOptions.end());
88
89 m_selectedOptions = selectedOptions;
90 m_selectedOptions.removeDuplicates();
91 std::sort(m_selectedOptions.begin(), m_selectedOptions.end());
92
93 m_invalidOptions = subtract(m_selectedOptions, m_validOptions);
94 const QStringList validSelectedOptions = subtract(m_selectedOptions, m_invalidOptions);
95 const QStringList validUnselectedOptions = subtract(m_validOptions, m_selectedOptions);
96
97 for (const QString &option : validSelectedOptions)
98 appendItem(option, true, true);
99
100 for (const QString &option : std::as_const(m_invalidOptions))
101 appendItem(option, false, true);
102
103 if ((validSelectedOptions.size() + m_invalidOptions.size()) && validUnselectedOptions.size())
104 appendSeparator();
105
106 for (const QString &option : validUnselectedOptions) {
107 appendItem(option, true, false);
108 if (option.isEmpty() && validUnselectedOptions.size() > 1) // special No Option item
109 appendSeparator();
110 }
111}
112
113void QOptionsWidget::setNoOptionText(const QString &text)
114{
115 if (m_noOptionText == text)
116 return;
117
118 m_noOptionText = text;
119
120 // update GUI
121 const auto itEnd = m_optionToItem.constEnd();
122 for (auto it = m_optionToItem.constBegin(); it != itEnd; ++it) {
123 const QString optionName = it.key();
124 if (optionName.isEmpty())
125 it.value()->setText(optionText(optionName, m_validOptions.contains(optionName)));
126 }
127}
128
129void QOptionsWidget::setInvalidOptionText(const QString &text)
130{
131 if (m_invalidOptionText == text)
132 return;
133
134 m_invalidOptionText = text;
135
136 // update GUI
137 for (const QString &option : std::as_const(m_invalidOptions))
138 m_optionToItem.value(option)->setText(optionText(option, false));
139}
140
141QString QOptionsWidget::optionText(const QString &optionName, bool valid) const
142{
143 QString text = optionName;
144 if (optionName.isEmpty())
145 text = u'[' + m_noOptionText + u']';
146 if (!valid)
147 text += "\t["_L1 + m_invalidOptionText + u']';
148 return text;
149}
150
151QListWidgetItem *QOptionsWidget::appendItem(const QString &optionName, bool valid, bool selected)
152{
153 QListWidgetItem *optionItem = new QListWidgetItem(optionText(optionName, valid), m_listWidget);
154 optionItem->setCheckState(selected ? Qt::Checked : Qt::Unchecked);
155 m_listWidget->addItem(optionItem);
156 m_optionToItem[optionName] = optionItem;
157 m_itemToOption[optionItem] = optionName;
158 return optionItem;
159}
160
161void QOptionsWidget::appendSeparator()
162{
163 QListWidgetItem *separatorItem = new QListWidgetItem(m_listWidget);
165 m_listWidget->addItem(separatorItem);
166}
167
168void QOptionsWidget::itemChanged(QListWidgetItem *item)
169{
170 const auto it = m_itemToOption.constFind(item);
171 if (it == m_itemToOption.constEnd())
172 return;
173
174 const QString option = *it;
175
176 if (item->checkState() == Qt::Checked && !m_selectedOptions.contains(option)) {
177 m_selectedOptions.append(option);
178 std::sort(m_selectedOptions.begin(), m_selectedOptions.end());
179 } else if (item->checkState() == Qt::Unchecked && m_selectedOptions.contains(option)) {
180 m_selectedOptions.removeOne(option);
181 } else {
182 return;
183 }
184 emit optionSelectionChanged(m_selectedOptions);
185}
186
187QT_END_NAMESPACE
static void setSeparator(QListWidgetItem *item)
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
This pure abstract function must be reimplemented if you want to provide custom rendering.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
This pure abstract function must be reimplemented if you want to provide custom rendering.
ListWidgetDelegate(QWidget *w)
static bool isSeparator(const QModelIndex &index)
void setInvalidOptionText(const QString &text)
void setOptions(const QStringList &validOptions, const QStringList &selectedOptions)
void setNoOptionText(const QString &text)
Combined button and popup list for selecting options.
static QStringList subtract(const QStringList &minuend, const QStringList &subtrahend)