1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qoptionswidget_p.h"
5
6#include <QtWidgets/QComboBox>
7#include <QtWidgets/QItemDelegate>
8#include <QtWidgets/QListWidget>
9#include <QtWidgets/QVBoxLayout>
10
11QT_BEGIN_NAMESPACE
12
13class ListWidgetDelegate : public QItemDelegate
14{
15public:
16 ListWidgetDelegate(QWidget *w) : QItemDelegate(w), m_widget(w) {}
17
18 static bool isSeparator(const QModelIndex &index) {
19 return index.data(arole: Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator");
20 }
21 static void setSeparator(QListWidgetItem *item) {
22 item->setData(role: Qt::AccessibleDescriptionRole, value: QString::fromLatin1(ba: "separator"));
23 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
24 }
25
26protected:
27 void paint(QPainter *painter,
28 const QStyleOptionViewItem &option,
29 const QModelIndex &index) const override {
30 if (isSeparator(index)) {
31 QRect rect = option.rect;
32 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(object: option.widget))
33 rect.setWidth(view->viewport()->width());
34 QStyleOption opt;
35 opt.rect = rect;
36 m_widget->style()->drawPrimitive(pe: QStyle::PE_IndicatorToolBarSeparator, opt: &opt, p: painter, w: m_widget);
37 } else {
38 QItemDelegate::paint(painter, option, index);
39 }
40 }
41
42 QSize sizeHint(const QStyleOptionViewItem &option,
43 const QModelIndex &index) const override {
44 if (isSeparator(index)) {
45 int pm = m_widget->style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth, option: nullptr, widget: m_widget);
46 return QSize(pm, pm);
47 }
48 return QItemDelegate::sizeHint(option, index);
49 }
50private:
51 QWidget *m_widget;
52};
53
54static QStringList subtract(const QStringList &minuend, const QStringList &subtrahend)
55{
56 QStringList result = minuend;
57 for (const QString &str : subtrahend)
58 result.removeOne(t: str);
59 return result;
60}
61
62QOptionsWidget::QOptionsWidget(QWidget *parent)
63 : QWidget(parent)
64 , m_noOptionText(tr(s: "No Option"))
65 , m_invalidOptionText(tr(s: "Invalid Option"))
66{
67 m_listWidget = new QListWidget(this);
68 m_listWidget->setItemDelegate(new ListWidgetDelegate(m_listWidget));
69 QVBoxLayout *layout = new QVBoxLayout(this);
70 layout->addWidget(m_listWidget);
71 layout->setContentsMargins(QMargins());
72
73 connect(sender: m_listWidget, signal: &QListWidget::itemChanged, context: this, slot: &QOptionsWidget::itemChanged);
74}
75
76void QOptionsWidget::clear()
77{
78 setOptions(validOptions: QStringList(), selectedOptions: QStringList());
79}
80
81void QOptionsWidget::setOptions(const QStringList &validOptions,
82 const QStringList &selectedOptions)
83{
84 m_listWidget->clear();
85 m_optionToItem.clear();
86 m_itemToOption.clear();
87
88 m_validOptions = validOptions;
89 m_validOptions.removeDuplicates();
90 std::sort(first: m_validOptions.begin(), last: m_validOptions.end());
91
92 m_selectedOptions = selectedOptions;
93 m_selectedOptions.removeDuplicates();
94 std::sort(first: m_selectedOptions.begin(), last: m_selectedOptions.end());
95
96 m_invalidOptions = subtract(minuend: m_selectedOptions, subtrahend: m_validOptions);
97 const QStringList validSelectedOptions = subtract(minuend: m_selectedOptions, subtrahend: m_invalidOptions);
98 const QStringList validUnselectedOptions = subtract(minuend: m_validOptions, subtrahend: m_selectedOptions);
99
100 for (const QString &option : validSelectedOptions)
101 appendItem(optionName: option, valid: true, selected: true);
102
103 for (const QString &option : m_invalidOptions)
104 appendItem(optionName: option, valid: false, selected: true);
105
106 if ((validSelectedOptions.size() + m_invalidOptions.size())
107 && validUnselectedOptions.size()) {
108 appendSeparator();
109 }
110
111 for (const QString &option : validUnselectedOptions) {
112 appendItem(optionName: option, valid: true, selected: false);
113 if (option.isEmpty() && validUnselectedOptions.size() > 1) // special No Option item
114 appendSeparator();
115 }
116}
117
118QStringList QOptionsWidget::validOptions() const
119{
120 return m_validOptions;
121}
122
123QStringList QOptionsWidget::selectedOptions() const
124{
125 return m_selectedOptions;
126}
127
128void QOptionsWidget::setNoOptionText(const QString &text)
129{
130 if (m_noOptionText == text)
131 return;
132
133 m_noOptionText = text;
134
135 // update GUI
136 const auto itEnd = m_optionToItem.constEnd();
137 for (auto it = m_optionToItem.constBegin(); it != itEnd; ++it) {
138 const QString optionName = it.key();
139 if (optionName.isEmpty())
140 it.value()->setText(optionText(optionName, valid: m_validOptions.contains(str: optionName)));
141 }
142}
143
144void QOptionsWidget::setInvalidOptionText(const QString &text)
145{
146 if (m_invalidOptionText == text)
147 return;
148
149 m_invalidOptionText = text;
150
151 // update GUI
152 for (const QString &option : m_invalidOptions)
153 m_optionToItem.value(key: option)->setText(optionText(optionName: option, valid: false));
154}
155
156QString QOptionsWidget::optionText(const QString &optionName, bool valid) const
157{
158 QString text = optionName;
159 if (optionName.isEmpty())
160 text = QLatin1Char('[') + m_noOptionText + QLatin1Char(']');
161 if (!valid)
162 text += QLatin1String("\t[") + m_invalidOptionText + QLatin1Char(']');
163 return text;
164}
165
166QListWidgetItem *QOptionsWidget::appendItem(const QString &optionName, bool valid, bool selected)
167{
168 QListWidgetItem *optionItem = new QListWidgetItem(optionText(optionName, valid), m_listWidget);
169 optionItem->setCheckState(selected ? Qt::Checked : Qt::Unchecked);
170 m_listWidget->insertItem(row: m_listWidget->count(), item: optionItem);
171 m_optionToItem[optionName] = optionItem;
172 m_itemToOption[optionItem] = optionName;
173 return optionItem;
174}
175
176void QOptionsWidget::appendSeparator()
177{
178 QListWidgetItem *separatorItem = new QListWidgetItem(m_listWidget);
179 ListWidgetDelegate::setSeparator(separatorItem);
180 m_listWidget->insertItem(row: m_listWidget->count(), item: separatorItem);
181}
182
183void QOptionsWidget::itemChanged(QListWidgetItem *item)
184{
185 const auto it = m_itemToOption.constFind(key: item);
186 if (it == m_itemToOption.constEnd())
187 return;
188
189 const QString option = *it;
190
191 if (item->checkState() == Qt::Checked && !m_selectedOptions.contains(str: option)) {
192 m_selectedOptions.append(t: option);
193 std::sort(first: m_selectedOptions.begin(), last: m_selectedOptions.end());
194 } else if (item->checkState() == Qt::Unchecked && m_selectedOptions.contains(str: option)) {
195 m_selectedOptions.removeOne(t: option);
196 } else {
197 return;
198 }
199
200 emit optionSelectionChanged(options: m_selectedOptions);
201}
202
203
204QT_END_NAMESPACE
205

source code of qttools/src/assistant/help/qoptionswidget.cpp