From cd86aa05d6e85e1f10e2c1c41205696b770ccd48 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 11 Dec 2019 15:55:03 +0100 Subject: [PATCH] Utils: Introduce InfoLabel Utils::InfoLabel is meant to replace around 30 custom implementations of a QHBoxLayout with an pixmap QLabel and a text QLabel. Instead of fiddling with pixmaps, the user of InfoLabel can set an enum for info/warning/etc... type. Internally, a globally shared QIcon is used. The usage of QIcon ensures that @1x, @2x and hybrid cases are handled. Task-number: QTCREATORBUG-23346 Change-Id: I0f91a21e64f095db14837512263c4becbb2c13d7 Reviewed-by: Christian Stenger --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/infolabel.cpp | 146 ++++++++++++++++++ src/libs/utils/infolabel.h | 63 ++++++++ src/libs/utils/utils-lib.pri | 6 +- src/libs/utils/utils.qbs | 2 + tests/manual/widgets/CMakeLists.txt | 1 + .../manual/widgets/crumblepath/CMakeLists.txt | 3 +- .../widgets/crumblepath/crumblepath.pro | 2 +- .../tst_manual_widgets_crumblepath.cpp | 2 +- .../tst_manual_widgets_crumblepath.qrc | 9 -- tests/manual/widgets/infolabel/CMakeLists.txt | 6 + tests/manual/widgets/infolabel/infolabel.pro | 13 ++ .../tst_manual_widgets_infolabel.cpp | 113 ++++++++++++++ tests/manual/widgets/themes.qrc | 10 ++ tests/manual/widgets/widgets.pro | 3 +- 15 files changed, 365 insertions(+), 15 deletions(-) create mode 100644 src/libs/utils/infolabel.cpp create mode 100644 src/libs/utils/infolabel.h delete mode 100644 tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.qrc create mode 100644 tests/manual/widgets/infolabel/CMakeLists.txt create mode 100644 tests/manual/widgets/infolabel/infolabel.pro create mode 100644 tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp create mode 100644 tests/manual/widgets/themes.qrc diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index cff2d415d93..c4402c236e8 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -79,6 +79,7 @@ add_qtc_library(Utils hostosinfo.cpp hostosinfo.h htmldocextractor.cpp htmldocextractor.h icon.cpp icon.h + infolabel.cpp infolabel.h itemviews.cpp itemviews.h json.cpp json.h jsontreeitem.cpp jsontreeitem.h diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp new file mode 100644 index 00000000000..d5adb32e57c --- /dev/null +++ b/src/libs/utils/infolabel.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "infolabel.h" + +#include +#include + +#include +#include +#include + +namespace Utils { + +constexpr int iconSize = 16; + +InfoLabel::InfoLabel(QWidget *parent) + : InfoLabel({}, Information, parent) +{ +} + +InfoLabel::InfoLabel(const QString &text, InfoType type, QWidget *parent) + : ElidingLabel(text, parent) +{ + setType(type); + setMinimumHeight(iconSize); +} + +InfoLabel::InfoType InfoLabel::type() const +{ + return m_type; +} + +void InfoLabel::setType(InfoType type) +{ + m_type = type; + setContentsMargins(m_type == None ? 0 : iconSize + 2, 0, 0, 0); + update(); +} + +bool InfoLabel::filled() const +{ + return m_filled; +} + +void InfoLabel::setFilled(bool filled) +{ + m_filled = filled; +} + +static Utils::Theme::Color fillColorForType(InfoLabel::InfoType type) +{ + using namespace Utils; + switch (type) { + case InfoLabel::Warning: + return Theme::IconsWarningColor; + case InfoLabel::Ok: + return Theme::IconsRunColor; + case InfoLabel::Error: + case InfoLabel::NotOk: + return Theme::IconsErrorColor; + case InfoLabel::Information: + default: + return Theme::IconsInfoColor; + } +} + +static const QIcon &iconForType(InfoLabel::InfoType type) +{ + using namespace Utils; + switch (type) { + case InfoLabel::Information: { + static const QIcon icon = Icons::INFO.icon(); + return icon; + } + case InfoLabel::Warning: { + static const QIcon icon = Icons::WARNING.icon(); + return icon; + } + case InfoLabel::Error: { + static const QIcon icon = Icons::CRITICAL.icon(); + return icon; + } + case InfoLabel::Ok: { + static const QIcon icon = Icons::OK.icon(); + return icon; + } + case InfoLabel::NotOk: { + static const QIcon icon = Icons::BROKEN.icon(); + return icon; + } + default: { + static const QIcon undefined; + return undefined; + } + } +} + +void InfoLabel::paintEvent(QPaintEvent *event) +{ + if (m_type == None) + return ElidingLabel::paintEvent(event); + + const bool centerIconVertically = wordWrap() || elideMode() == Qt::ElideNone; + const QRect iconRect(0, centerIconVertically ? 0 : ((height() - iconSize) / 2), + iconSize, iconSize); + + QPainter p(this); + if (m_filled && isEnabled()) { + p.save(); + p.setOpacity(0.175); + p.fillRect(rect(), creatorTheme()->color(fillColorForType(m_type))); + p.restore(); + } + const QIcon &icon = iconForType(m_type); + QWindow *window = this->window()->windowHandle(); + const QIcon::Mode mode = !this->isEnabled() ? QIcon::Disabled : QIcon::Normal; + const QPixmap iconPx = + icon.pixmap(window, QSize(iconSize, iconSize) * devicePixelRatio(), mode); + p.drawPixmap(iconRect, iconPx); + ElidingLabel::paintEvent(event); +} + +} // namespace Utils diff --git a/src/libs/utils/infolabel.h b/src/libs/utils/infolabel.h new file mode 100644 index 00000000000..4718ca092c9 --- /dev/null +++ b/src/libs/utils/infolabel.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "utils_global.h" + +#include "elidinglabel.h" + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT InfoLabel : public ElidingLabel +{ +public: + enum InfoType { + Information, + Warning, + Error, + Ok, + NotOk, + None + }; + + explicit InfoLabel(QWidget *parent); + explicit InfoLabel(const QString &text = {}, InfoType type = Information, + QWidget *parent = nullptr); + + InfoType type() const; + void setType(InfoType type); + bool filled() const; + void setFilled(bool filled); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + InfoType m_type = Information; + bool m_filled = false; +}; + +} // namespace Utils diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index e2a33060818..bd02ae6828c 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -132,7 +132,8 @@ SOURCES += \ $$PWD/differ.cpp \ $$PWD/jsontreeitem.cpp \ $$PWD/namevaluevalidator.cpp \ - $$PWD/camelcasecursor.cpp + $$PWD/camelcasecursor.cpp \ + $$PWD/infolabel.cpp HEADERS += \ $$PWD/environmentfwd.h \ @@ -279,7 +280,8 @@ HEADERS += \ $$PWD/jsontreeitem.h \ $$PWD/listmodel.h \ $$PWD/namevaluevalidator.h \ - $$PWD/camelcasecursor.h + $$PWD/camelcasecursor.h \ + $$PWD/infolabel.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/newclasswidget.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 10ddfe1d07c..0f2b57d07a2 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -141,6 +141,8 @@ Project { "htmldocextractor.h", "icon.cpp", "icon.h", + "infolabel.cpp", + "infolabel.h", "itemviews.cpp", "itemviews.h", "json.cpp", diff --git a/tests/manual/widgets/CMakeLists.txt b/tests/manual/widgets/CMakeLists.txt index 3a529bd91e7..03138403622 100644 --- a/tests/manual/widgets/CMakeLists.txt +++ b/tests/manual/widgets/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(crumblepath) +add_subdirectory(infolabel) diff --git a/tests/manual/widgets/crumblepath/CMakeLists.txt b/tests/manual/widgets/crumblepath/CMakeLists.txt index 01dffb33618..ac36a76bfd2 100644 --- a/tests/manual/widgets/crumblepath/CMakeLists.txt +++ b/tests/manual/widgets/crumblepath/CMakeLists.txt @@ -1,5 +1,6 @@ add_qtc_executable(tst_manual_widgets_crumblepath DEPENDS Qt5::Gui Utils Core SOURCES - tst_manual_widgets_crumblepath.cpp tst_manual_widgets_crumblepath.qrc + tst_manual_widgets_crumblepath.cpp + ../themes.qrc ) diff --git a/tests/manual/widgets/crumblepath/crumblepath.pro b/tests/manual/widgets/crumblepath/crumblepath.pro index 6645136aaf8..555b63c9a02 100644 --- a/tests/manual/widgets/crumblepath/crumblepath.pro +++ b/tests/manual/widgets/crumblepath/crumblepath.pro @@ -2,7 +2,7 @@ SOURCES += \ tst_manual_widgets_crumblepath.cpp RESOURCES += \ - tst_manual_widgets_crumblepath.qrc + ../themes.qrc QTC_LIB_DEPENDS += \ utils diff --git a/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.cpp b/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.cpp index b2815cea833..f7dcad82221 100644 --- a/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.cpp +++ b/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.cpp @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); Theme theme(""); - QSettings settings(":/flat.creatortheme", QSettings::IniFormat); + QSettings settings(":/themes/flat.creatortheme", QSettings::IniFormat); theme.readSettings(settings); setCreatorTheme(&theme); StyleHelper::setBaseColor(QColor(StyleHelper::DEFAULT_BASE_COLOR)); diff --git a/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.qrc b/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.qrc deleted file mode 100644 index d596e9c0ebf..00000000000 --- a/tests/manual/widgets/crumblepath/tst_manual_widgets_crumblepath.qrc +++ /dev/null @@ -1,9 +0,0 @@ - - - ../../../../share/qtcreator/themes/dark.creatortheme - ../../../../share/qtcreator/themes/default.creatortheme - ../../../../share/qtcreator/themes/flat-dark.creatortheme - ../../../../share/qtcreator/themes/flat-light.creatortheme - ../../../../share/qtcreator/themes/flat.creatortheme - - diff --git a/tests/manual/widgets/infolabel/CMakeLists.txt b/tests/manual/widgets/infolabel/CMakeLists.txt new file mode 100644 index 00000000000..510261ec040 --- /dev/null +++ b/tests/manual/widgets/infolabel/CMakeLists.txt @@ -0,0 +1,6 @@ +add_qtc_executable(tst_manual_widgets_infolabel + DEPENDS Qt5::Gui Utils Core + SOURCES + tst_manual_widgets_infolabel.cpp + ../themes.qrc +) diff --git a/tests/manual/widgets/infolabel/infolabel.pro b/tests/manual/widgets/infolabel/infolabel.pro new file mode 100644 index 00000000000..a38b3c8e757 --- /dev/null +++ b/tests/manual/widgets/infolabel/infolabel.pro @@ -0,0 +1,13 @@ +SOURCES += \ + tst_manual_widgets_infolabel.cpp + +RESOURCES += \ + ../themes.qrc + +QTC_LIB_DEPENDS += \ + utils + +QTC_PLUGIN_DEPENDS += \ + coreplugin + +include(../../../auto/qttest.pri) diff --git a/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp b/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp new file mode 100644 index 00000000000..1b4a4061c31 --- /dev/null +++ b/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Utils; + +int main(int argc, char *argv[]) +{ + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QApplication app(argc, argv); + + Theme theme(""); + QSettings settings(":/themes/flat.creatortheme", QSettings::IniFormat); + theme.readSettings(settings); + setCreatorTheme(&theme); + StyleHelper::setBaseColor(QColor(StyleHelper::DEFAULT_BASE_COLOR)); + QApplication::setStyle(new ManhattanStyle(creatorTheme()->preferredStyles().value(0))); + + auto widget = new QWidget; + auto mainLayout = new QVBoxLayout(widget); + auto variationsLayout = new QGridLayout; + mainLayout->addLayout(variationsLayout); + + const static struct { + const InfoLabel::InfoType type; + const char *text; + } labels[] = { + {InfoLabel::Information, "Information"}, + {InfoLabel::Warning, "Warning"}, + {InfoLabel::Error, "Error"}, + {InfoLabel::Ok, "Ok"}, + {InfoLabel::NotOk, "NotOk"}, + {InfoLabel::None, "None"}, + }; + + int gridRow = 0; + for (auto filled : {false, true}) { + for (auto label : labels) { + for (auto enabled : {true, false}) { + auto infoLabel = new Utils::InfoLabel( + label.text + QLatin1String(filled ? " (filled)" : "") + + QLatin1String(enabled ? "" : " (disabled)"), label.type); + infoLabel->setEnabled(enabled); + infoLabel->setFilled(filled); + variationsLayout->addWidget(infoLabel, gridRow, enabled ? 0 : 1); + } + gridRow++; + } + variationsLayout->addItem(new QSpacerItem(0, 10), gridRow++, 0); + } + + auto withLink = new Utils::InfoLabel("With link", InfoLabel::Error); + withLink->setElideMode(Qt::ElideNone); + QObject::connect(withLink, &QLabel::linkActivated, [widget](const QString& link){ + QMessageBox::information(widget, {}, link); + }); + mainLayout->addWidget(withLink); + + auto stretching = new Utils::InfoLabel("Stretching and centering vertically", InfoLabel::Warning); + stretching->setFilled(true); + mainLayout->addWidget(stretching, 2); + + const QString lorem = + "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt " + "ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation " + "ullamco laboris nisi ut aliquid ex ea commodi consequat."; + + mainLayout->addWidget(new Utils::InfoLabel("Qt::ElideRight: " + lorem, InfoLabel::Information)); + + auto elideNone = new Utils::InfoLabel("Qt::ElideNone: " + lorem, InfoLabel::Information); + elideNone->setElideMode(Qt::ElideNone); + elideNone->setWordWrap(true); + mainLayout->addWidget(elideNone); + + widget->resize(350, 500); + widget->show(); + + return app.exec(); +} diff --git a/tests/manual/widgets/themes.qrc b/tests/manual/widgets/themes.qrc new file mode 100644 index 00000000000..c6fe4b1d1aa --- /dev/null +++ b/tests/manual/widgets/themes.qrc @@ -0,0 +1,10 @@ + + + ../../../share/qtcreator/themes/dark.creatortheme + ../../../share/qtcreator/themes/default.creatortheme + ../../../share/qtcreator/themes/flat-dark.creatortheme + ../../../share/qtcreator/themes/flat-light.creatortheme + ../../../share/qtcreator/themes/flat.creatortheme + ../../../share/qtcreator/themes/design.creatortheme + + diff --git a/tests/manual/widgets/widgets.pro b/tests/manual/widgets/widgets.pro index aa0a4e874e2..00f36c1c94f 100644 --- a/tests/manual/widgets/widgets.pro +++ b/tests/manual/widgets/widgets.pro @@ -1,3 +1,4 @@ TEMPLATE = subdirs SUBDIRS = \ - crumblepath + crumblepath \ + infolabel