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 | |
95 | static inline QString className(const QObject *o) |
96 | { |
97 | return QString::fromUtf8(o->metaObject()->className()); |
98 | } |
99 | |
100 | static inline void setClassNameToolTip(QWidget *w) |
101 | { |
102 | w->setToolTip(className(w)); |
103 | } |
104 | |
105 | static 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 | |
113 | static inline QString helpUrl(const QWidget *w) |
114 | { |
115 | return helpUrl(className(w).toLower()); |
116 | } |
117 | |
118 | static void launchHelp(const QWidget *w) |
119 | { |
120 | QDesktopServices::openUrl(helpUrl(w)); |
121 | } |
122 | |
123 | static void launchModuleHelp() |
124 | { |
125 | QDesktopServices::openUrl(helpUrl(QLatin1String("qtwidgets-index" ))); |
126 | } |
127 | |
128 | template <class Widget> |
129 | Widget *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 | |
137 | template <class Widget, class Parameter> |
138 | Widget *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 | |
146 | QTextStream &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 | |
153 | static 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 | |
165 | WidgetGallery::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 | |
232 | void 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 | |
241 | void WidgetGallery::changeStyle(const QString &styleName) |
242 | { |
243 | QApplication::setStyle(QStyleFactory::create(styleName)); |
244 | } |
245 | |
246 | void WidgetGallery::advanceProgressBar() |
247 | { |
248 | int curVal = progressBar->value(); |
249 | int maxVal = progressBar->maximum(); |
250 | progressBar->setValue(curVal + (maxVal - curVal) / 100); |
251 | } |
252 | |
253 | QGroupBox *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 = createWidget<QToolButton>("menuButton" ); |
271 | menuToolButton->setText(tr("Menu Button" )); |
272 | auto = 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 | |
317 | static 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 | |
326 | QToolBox *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 | |
353 | QTabWidget *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 | |
387 | QGroupBox *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 | |
427 | QProgressBar *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 | |
439 | void 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 | |
464 | void 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 | |