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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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