1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "widgetgallery.h"
52
53#include <QApplication>
54#include <QCheckBox>
55#include <QComboBox>
56#include <QCommandLinkButton>
57#include <QDateTimeEdit>
58#include <QDial>
59#include <QDialogButtonBox>
60#include <QFileSystemModel>
61#include <QGridLayout>
62#include <QGroupBox>
63#include <QMenu>
64#include <QLabel>
65#include <QLineEdit>
66#include <QListWidget>
67#include <QPlainTextEdit>
68#include <QProgressBar>
69#include <QPushButton>
70#include <QRadioButton>
71#include <QScrollBar>
72#include <QShortcut>
73#include <QSpinBox>
74#include <QStandardItemModel>
75#include <QStyle>
76#include <QStyleFactory>
77#include <QTextBrowser>
78#include <QTreeView>
79#include <QTableWidget>
80#include <QTextEdit>
81#include <QToolBox>
82#include <QToolButton>
83
84#include <QIcon>
85#include <QDesktopServices>
86#include <QScreen>
87#include <QWindow>
88
89#include <QDebug>
90#include <QLibraryInfo>
91#include <QSysInfo>
92#include <QTextStream>
93#include <QTimer>
94
95static inline QString className(const QObject *o)
96{
97 return QString::fromUtf8(o->metaObject()->className());
98}
99
100static inline void setClassNameToolTip(QWidget *w)
101{
102 w->setToolTip(className(w));
103}
104
105static QString helpUrl(const QString &page)
106{
107 QString result;
108 QTextStream(&result) << "https://doc.qt.io/qt-" << QT_VERSION_MAJOR
109 << '/' << page << ".html";
110 return result;
111}
112
113static inline QString helpUrl(const QWidget *w)
114{
115 return helpUrl(className(w).toLower());
116}
117
118static void launchHelp(const QWidget *w)
119{
120 QDesktopServices::openUrl(helpUrl(w));
121}
122
123static void launchModuleHelp()
124{
125 QDesktopServices::openUrl(helpUrl(QLatin1String("qtwidgets-index")));
126}
127
128template <class Widget>
129Widget *createWidget(const char *name, QWidget *parent = nullptr)
130{
131 auto result = new Widget(parent);
132 result->setObjectName(QLatin1String(name));
133 setClassNameToolTip(result);
134 return result;
135}
136
137template <class Widget, class Parameter>
138Widget *createWidget1(const Parameter &p1, const char *name, QWidget *parent = nullptr)
139{
140 auto result = new Widget(p1, parent);
141 result->setObjectName(QLatin1String(name));
142 setClassNameToolTip(result);
143 return result;
144}
145
146QTextStream &operator<<(QTextStream &str, const QRect &r)
147{
148 str << r.width() << 'x' << r.height() << Qt::forcesign << r.x() << r.y()
149 << Qt::noforcesign;
150 return str;
151}
152
153static QString highDpiScaleFactorRoundingPolicy()
154{
155 QString result;
156 QDebug(&result) << QGuiApplication::highDpiScaleFactorRoundingPolicy();
157 if (result.endsWith(QLatin1Char(')')))
158 result.chop(1);
159 const int lastSep = result.lastIndexOf(QLatin1String("::"));
160 if (lastSep != -1)
161 result.remove(0, lastSep + 2);
162 return result;
163}
164
165WidgetGallery::WidgetGallery(QWidget *parent)
166 : QDialog(parent)
167 , progressBar(createProgressBar())
168{
169 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
170
171 auto styleComboBox = createWidget<QComboBox>("styleComboBox");
172 const QString defaultStyleName = QApplication::style()->objectName();
173 QStringList styleNames = QStyleFactory::keys();
174 for (int i = 1, size = styleNames.size(); i < size; ++i) {
175 if (defaultStyleName.compare(styleNames.at(i), Qt::CaseInsensitive) == 0) {
176 styleNames.swapItemsAt(0, i);
177 break;
178 }
179 }
180 styleComboBox->addItems(styleNames);
181
182 auto styleLabel = createWidget1<QLabel>(tr("&Style:"), "styleLabel");
183 styleLabel->setBuddy(styleComboBox);
184
185 auto helpLabel = createWidget1<QLabel>(tr("Press F1 over a widget to see Documentation"), "helpLabel");
186
187 auto disableWidgetsCheckBox = createWidget1<QCheckBox>(tr("&Disable widgets"), "disableWidgetsCheckBox");
188
189 auto buttonsGroupBox = createButtonsGroupBox();
190 auto itemViewTabWidget = createItemViewTabWidget();
191 auto simpleInputWidgetsGroupBox = createSimpleInputWidgetsGroupBox();
192 auto textToolBox = createTextToolBox();
193
194 connect(styleComboBox, &QComboBox::textActivated,
195 this, &WidgetGallery::changeStyle);
196 connect(disableWidgetsCheckBox, &QCheckBox::toggled,
197 buttonsGroupBox, &QWidget::setDisabled);
198 connect(disableWidgetsCheckBox, &QCheckBox::toggled,
199 textToolBox, &QWidget::setDisabled);
200 connect(disableWidgetsCheckBox, &QCheckBox::toggled,
201 itemViewTabWidget, &QWidget::setDisabled);
202 connect(disableWidgetsCheckBox, &QCheckBox::toggled,
203 simpleInputWidgetsGroupBox, &QWidget::setDisabled);
204
205 auto topLayout = new QHBoxLayout;
206 topLayout->addWidget(styleLabel);
207 topLayout->addWidget(styleComboBox);
208 topLayout->addStretch(1);
209 topLayout->addWidget(helpLabel);
210 topLayout->addStretch(1);
211 topLayout->addWidget(disableWidgetsCheckBox);
212
213 auto dialogButtonBox = createWidget1<QDialogButtonBox>(QDialogButtonBox::Help | QDialogButtonBox::Close,
214 "dialogButtonBox");
215 connect(dialogButtonBox, &QDialogButtonBox::helpRequested, this, launchModuleHelp);
216 connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
217
218 auto mainLayout = new QGridLayout(this);
219 mainLayout->addLayout(topLayout, 0, 0, 1, 2);
220 mainLayout->addWidget(buttonsGroupBox, 1, 0);
221 mainLayout->addWidget(simpleInputWidgetsGroupBox, 1, 1);
222 mainLayout->addWidget(itemViewTabWidget, 2, 0);
223 mainLayout->addWidget(textToolBox, 2, 1);
224 mainLayout->addWidget(progressBar, 3, 0, 1, 2);
225 mainLayout->addWidget(dialogButtonBox, 4, 0, 1, 2);
226
227 setWindowTitle(tr("Widget Gallery Qt %1").arg(QT_VERSION_STR));
228
229 new QShortcut(QKeySequence::HelpContents, this, this, &WidgetGallery::helpOnCurrentWidget);
230}
231
232void WidgetGallery::setVisible(bool visible)
233{
234 QDialog::setVisible(visible);
235 if (visible) {
236 connect(windowHandle(), &QWindow::screenChanged, this, &WidgetGallery::updateSystemInfo);
237 updateSystemInfo();
238 }
239}
240
241void WidgetGallery::changeStyle(const QString &styleName)
242{
243 QApplication::setStyle(QStyleFactory::create(styleName));
244}
245
246void WidgetGallery::advanceProgressBar()
247{
248 int curVal = progressBar->value();
249 int maxVal = progressBar->maximum();
250 progressBar->setValue(curVal + (maxVal - curVal) / 100);
251}
252
253QGroupBox *WidgetGallery::createButtonsGroupBox()
254{
255 auto result = createWidget1<QGroupBox>(tr("Buttons"), "buttonsGroupBox");
256
257 auto defaultPushButton = createWidget1<QPushButton>(tr("Default Push Button"), "defaultPushButton");
258 defaultPushButton->setDefault(true);
259
260 auto togglePushButton = createWidget1<QPushButton>(tr("Toggle Push Button"), "togglePushButton");
261 togglePushButton->setCheckable(true);
262 togglePushButton->setChecked(true);
263
264 auto flatPushButton = createWidget1<QPushButton>(tr("Flat Push Button"), "flatPushButton");
265 flatPushButton->setFlat(true);
266
267 auto toolButton = createWidget<QToolButton>("toolButton");
268 toolButton->setText(tr("Tool Button"));
269
270 auto menuToolButton = createWidget<QToolButton>("menuButton");
271 menuToolButton->setText(tr("Menu Button"));
272 auto toolMenu = new QMenu(menuToolButton);
273 menuToolButton->setPopupMode(QToolButton::InstantPopup);
274 toolMenu->addAction("Option");
275 toolMenu->addSeparator();
276 auto action = toolMenu->addAction("Checkable Option");
277 action->setCheckable(true);
278 menuToolButton->setMenu(toolMenu);
279 auto toolLayout = new QHBoxLayout;
280 toolLayout->addWidget(toolButton);
281 toolLayout->addWidget(menuToolButton);
282
283 auto commandLinkButton = createWidget1<QCommandLinkButton>(tr("Command Link Button"), "commandLinkButton");
284 commandLinkButton->setDescription(tr("Description"));
285
286 auto buttonLayout = new QVBoxLayout;
287 buttonLayout->addWidget(defaultPushButton);
288 buttonLayout->addWidget(togglePushButton);
289 buttonLayout->addWidget(flatPushButton);
290 buttonLayout->addLayout(toolLayout);
291 buttonLayout->addWidget(commandLinkButton);
292 buttonLayout->addStretch(1);
293
294 auto radioButton1 = createWidget1<QRadioButton>(tr("Radio button 1"), "radioButton1");
295 auto radioButton2 = createWidget1<QRadioButton>(tr("Radio button 2"), "radioButton2");
296 auto radioButton3 = createWidget1<QRadioButton>(tr("Radio button 3"), "radioButton3");
297 radioButton1->setChecked(true);
298
299 auto checkBox = createWidget1<QCheckBox>(tr("Tri-state check box"), "checkBox");
300 checkBox->setTristate(true);
301 checkBox->setCheckState(Qt::PartiallyChecked);
302
303 auto checkableLayout = new QVBoxLayout;
304 checkableLayout->addWidget(radioButton1);
305 checkableLayout->addWidget(radioButton2);
306 checkableLayout->addWidget(radioButton3);
307 checkableLayout->addWidget(checkBox);
308 checkableLayout->addStretch(1);
309
310 auto mainLayout = new QHBoxLayout(result);
311 mainLayout->addLayout(buttonLayout);
312 mainLayout->addLayout(checkableLayout);
313 mainLayout->addStretch();
314 return result;
315}
316
317static QWidget *embedIntoHBoxLayout(QWidget *w, int margin = 5)
318{
319 auto result = new QWidget;
320 auto layout = new QHBoxLayout(result);
321 layout->setContentsMargins(margin, margin, margin, margin);
322 layout->addWidget(w);
323 return result;
324}
325
326QToolBox *WidgetGallery::createTextToolBox()
327{
328 auto result = createWidget<QToolBox>("toolBox");
329
330 const QString plainText = tr("Twinkle, twinkle, little star,\n"
331 "How I wonder what you are.\n"
332 "Up above the world so high,\n"
333 "Like a diamond in the sky.\n"
334 "Twinkle, twinkle, little star,\n"
335 "How I wonder what you are!\n");
336 // Create centered/italic HTML rich text
337 QString richText = QLatin1String("<html><head/><body><i>");
338 for (const auto &line : QStringView{ plainText }.split(QLatin1Char('\n')))
339 richText += QString::fromLatin1("<center>%1</center>").arg(line);
340 richText += QLatin1String("</i></body></html>");
341
342 auto textEdit = createWidget1<QTextEdit>(richText, "textEdit");
343 auto plainTextEdit = createWidget1<QPlainTextEdit>(plainText, "plainTextEdit");
344
345 systemInfoTextBrowser = createWidget<QTextBrowser>("systemInfoTextBrowser");
346
347 result->addItem(embedIntoHBoxLayout(textEdit), tr("Text Edit"));
348 result->addItem(embedIntoHBoxLayout(plainTextEdit), tr("Plain Text Edit"));
349 result->addItem(embedIntoHBoxLayout(systemInfoTextBrowser), tr("Text Browser"));
350 return result;
351}
352
353QTabWidget *WidgetGallery::createItemViewTabWidget()
354{
355 auto result = createWidget<QTabWidget>("bottomLeftTabWidget");
356 result->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
357
358 auto treeView = createWidget<QTreeView>("treeView");
359 auto fileSystemModel = new QFileSystemModel(treeView);
360 fileSystemModel->setRootPath(QDir::rootPath());
361 treeView->setModel(fileSystemModel);
362
363 auto tableWidget = createWidget<QTableWidget>("tableWidget");
364 tableWidget->setRowCount(10);
365 tableWidget->setColumnCount(10);
366
367 auto listModel = new QStandardItemModel(0, 1, result);
368 listModel->appendRow(new QStandardItem(QIcon(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-128.png")),
369 tr("Directory")));
370 listModel->appendRow(new QStandardItem(QIcon(QLatin1String(":/qt-project.org/styles/commonstyle/images/computer-32.png")),
371 tr("Computer")));
372
373 auto listView = createWidget<QListView>("listView");
374 listView->setModel(listModel);
375
376 auto iconModeListView = createWidget<QListView>("iconModeListView");
377 iconModeListView->setViewMode(QListView::IconMode);
378 iconModeListView->setModel(listModel);
379
380 result->addTab(embedIntoHBoxLayout(treeView), tr("&Tree View"));
381 result->addTab(embedIntoHBoxLayout(tableWidget), tr("T&able"));
382 result->addTab(embedIntoHBoxLayout(listView), tr("&List"));
383 result->addTab(embedIntoHBoxLayout(iconModeListView), tr("&Icon Mode List"));
384 return result;
385}
386
387QGroupBox *WidgetGallery::createSimpleInputWidgetsGroupBox()
388{
389 auto result = createWidget1<QGroupBox>(tr("Simple Input Widgets"), "bottomRightGroupBox");
390 result->setCheckable(true);
391 result->setChecked(true);
392
393 auto lineEdit = createWidget1<QLineEdit>("s3cRe7", "lineEdit");
394 lineEdit->setClearButtonEnabled(true);
395 lineEdit->setEchoMode(QLineEdit::Password);
396
397 auto spinBox = createWidget<QSpinBox>("spinBox", result);
398 spinBox->setValue(50);
399
400 auto dateTimeEdit = createWidget<QDateTimeEdit>("dateTimeEdit", result);
401 dateTimeEdit->setDateTime(QDateTime::currentDateTime());
402
403 auto slider = createWidget<QSlider>("slider", result);
404 slider->setOrientation(Qt::Horizontal);
405 slider->setValue(40);
406
407 auto scrollBar = createWidget<QScrollBar>("scrollBar", result);
408 scrollBar->setOrientation(Qt::Horizontal);
409 setClassNameToolTip(scrollBar);
410 scrollBar->setValue(60);
411
412 auto dial = createWidget<QDial>("dial", result);
413 dial->setValue(30);
414 dial->setNotchesVisible(true);
415
416 auto layout = new QGridLayout(result);
417 layout->addWidget(lineEdit, 0, 0, 1, 2);
418 layout->addWidget(spinBox, 1, 0, 1, 2);
419 layout->addWidget(dateTimeEdit, 2, 0, 1, 2);
420 layout->addWidget(slider, 3, 0);
421 layout->addWidget(scrollBar, 4, 0);
422 layout->addWidget(dial, 3, 1, 2, 1);
423 layout->setRowStretch(5, 1);
424 return result;
425}
426
427QProgressBar *WidgetGallery::createProgressBar()
428{
429 auto result = createWidget<QProgressBar>("progressBar");
430 result->setRange(0, 10000);
431 result->setValue(0);
432
433 auto timer = new QTimer(this);
434 connect(timer, &QTimer::timeout, this, &WidgetGallery::advanceProgressBar);
435 timer->start(1000);
436 return result;
437}
438
439void WidgetGallery::updateSystemInfo()
440{
441 QString systemInfo;
442 QTextStream str(&systemInfo);
443 str << "<html><head/><body><h3>Build</h3><p>" << QLibraryInfo::build() << "</p>"
444 << "<h3>Operating System</h3><p>" << QSysInfo::prettyProductName() << "</p>"
445 << "<h3>Screens</h3><p>High DPI scale factor rounding policy: "
446 << highDpiScaleFactorRoundingPolicy() << "</p><ol>";
447 const auto screens = QGuiApplication::screens();
448 for (auto screen : screens) {
449 const bool current = screen == this->screen();
450 str << "<li>";
451 if (current)
452 str << "<i>";
453 str << '"' << screen->name() << "\" " << screen->geometry() << ", "
454 << screen->logicalDotsPerInchX() << "DPI, DPR="
455 << screen->devicePixelRatio();
456 if (current)
457 str << "</i>";
458 str << "</li>";
459 }
460 str << "</ol></body></html>";
461 systemInfoTextBrowser->setHtml(systemInfo);
462}
463
464void WidgetGallery::helpOnCurrentWidget()
465{
466 // Skip over internal widgets
467 for (auto w = QApplication::widgetAt(QCursor::pos(screen())); w; w = w->parentWidget()) {
468 const QString name = w->objectName();
469 if (!name.isEmpty() && !name.startsWith(QLatin1String("qt_"))) {
470 launchHelp(w);
471 break;
472 }
473 }
474}
475