/**************************************************************************** ** ** Copyright (C) 2020 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 "infobar.h" #include "algorithm.h" #include "qtcassert.h" #include "qtcsettings.h" #include "utilsicons.h" #include #include #include #include #include #include #include static const char C_SUPPRESSED_WARNINGS[] = "SuppressedWarnings"; namespace Utils { QSet InfoBar::globallySuppressed; QSettings *InfoBar::m_settings = nullptr; class InfoBarWidget : public QWidget { public: InfoBarWidget(Qt::Edge edge, QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; private: const Qt::Edge m_edge; }; InfoBarWidget::InfoBarWidget(Qt::Edge edge, QWidget *parent) : QWidget(parent) , m_edge(edge) { const bool topEdge = m_edge == Qt::TopEdge; setContentsMargins(2, topEdge ? 0 : 1, 0, topEdge ? 1 : 0); } void InfoBarWidget::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); QPainter p(this); p.fillRect(rect(), creatorTheme()->color(Theme::InfoBarBackground)); const QRectF adjustedRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5); const bool topEdge = m_edge == Qt::TopEdge; p.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor)); p.drawLine(QLineF(topEdge ? adjustedRect.bottomLeft() : adjustedRect.topLeft(), topEdge ? adjustedRect.bottomRight() : adjustedRect.topRight())); } InfoBarEntry::InfoBarEntry(Id _id, const QString &_infoText, GlobalSuppression _globalSuppression) : m_id(_id) , m_infoText(_infoText) , m_globalSuppression(_globalSuppression) { } Id InfoBarEntry::id() const { return m_id; } QString InfoBarEntry::text() const { return m_infoText; } void InfoBarEntry::addCustomButton(const QString &buttonText, CallBack callBack, const QString &tooltip) { m_buttons.append({buttonText, callBack, tooltip}); } void InfoBarEntry::setCancelButtonInfo(CallBack callBack) { m_useCancelButton = true; m_cancelButtonCallBack = callBack; } void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack) { m_useCancelButton = true; m_cancelButtonText = _cancelButtonText; m_cancelButtonCallBack = callBack; } void InfoBarEntry::setComboInfo(const QStringList &list, ComboCallBack callBack, const QString &tooltip, int currentIndex) { auto comboInfos = transform(list, [](const QString &string) { return ComboInfo{string, string}; }); setComboInfo(comboInfos, callBack, tooltip, currentIndex); } void InfoBarEntry::setComboInfo(const QList &list, ComboCallBack callBack, const QString &tooltip, int currentIndex) { m_combo.entries = list; m_combo.callback = callBack; m_combo.tooltip = tooltip; m_combo.currentIndex = currentIndex; } void InfoBarEntry::removeCancelButton() { m_useCancelButton = false; m_cancelButtonText.clear(); m_cancelButtonCallBack = nullptr; } void InfoBarEntry::setDetailsWidgetCreator(const InfoBarEntry::DetailsWidgetCreator &creator) { m_detailsWidgetCreator = creator; } void InfoBar::addInfo(const InfoBarEntry &info) { m_infoBarEntries << info; emit changed(); } void InfoBar::removeInfo(Id id) { const int size = m_infoBarEntries.size(); Utils::erase(m_infoBarEntries, equal(&InfoBarEntry::m_id, id)); if (size != m_infoBarEntries.size()) emit changed(); } bool InfoBar::containsInfo(Id id) const { return anyOf(m_infoBarEntries, equal(&InfoBarEntry::m_id, id)); } // Remove and suppress id void InfoBar::suppressInfo(Id id) { removeInfo(id); m_suppressed << id; } // Info cannot be added more than once, or if it is suppressed bool InfoBar::canInfoBeAdded(Id id) const { return !containsInfo(id) && !m_suppressed.contains(id) && !globallySuppressed.contains(id); } void InfoBar::unsuppressInfo(Id id) { m_suppressed.remove(id); } void InfoBar::clear() { if (!m_infoBarEntries.isEmpty()) { m_infoBarEntries.clear(); emit changed(); } } void InfoBar::globallySuppressInfo(Id id) { globallySuppressed.insert(id); writeGloballySuppressedToSettings(); } void InfoBar::globallyUnsuppressInfo(Id id) { globallySuppressed.remove(id); writeGloballySuppressedToSettings(); } void InfoBar::initialize(QSettings *settings) { m_settings = settings; if (QTC_GUARD(m_settings)) { const QStringList list = m_settings->value(QLatin1String(C_SUPPRESSED_WARNINGS)).toStringList(); globallySuppressed = transform(list, Id::fromString); } } QSettings *InfoBar::settings() { return m_settings; } void InfoBar::clearGloballySuppressed() { globallySuppressed.clear(); if (m_settings) m_settings->remove(C_SUPPRESSED_WARNINGS); } bool InfoBar::anyGloballySuppressed() { return !globallySuppressed.isEmpty(); } void InfoBar::writeGloballySuppressedToSettings() { if (!m_settings) return; const QStringList list = transform(globallySuppressed, &Id::toString); QtcSettings::setValueWithDefault(m_settings, C_SUPPRESSED_WARNINGS, list); } InfoBarDisplay::InfoBarDisplay(QObject *parent) : QObject(parent) { } void InfoBarDisplay::setTarget(QBoxLayout *layout, int index) { m_boxLayout = layout; m_boxIndex = index; } void InfoBarDisplay::setInfoBar(InfoBar *infoBar) { if (m_infoBar == infoBar) return; if (m_infoBar) m_infoBar->disconnect(this); m_infoBar = infoBar; if (m_infoBar) { connect(m_infoBar, &InfoBar::changed, this, &InfoBarDisplay::update); connect(m_infoBar, &QObject::destroyed, this, &InfoBarDisplay::infoBarDestroyed); } update(); } void InfoBarDisplay::setEdge(Qt::Edge edge) { m_edge = edge; update(); } InfoBar *InfoBarDisplay::infoBar() const { return m_infoBar; } void InfoBarDisplay::infoBarDestroyed() { m_infoBar = nullptr; // Calling update() here causes a complicated crash on shutdown. // So instead we rely on the view now being either destroyed (in which case it // will delete the widgets itself) or setInfoBar() being called explicitly. } void InfoBarDisplay::update() { for (QWidget *widget : qAsConst(m_infoWidgets)) { widget->disconnect(this); // We want no destroyed() signal now delete widget; } m_infoWidgets.clear(); if (!m_infoBar) return; for (const InfoBarEntry &info : qAsConst(m_infoBar->m_infoBarEntries)) { auto infoWidget = new InfoBarWidget(m_edge); auto hbox = new QHBoxLayout; hbox->setContentsMargins(2, 2, 2, 2); auto vbox = new QVBoxLayout(infoWidget); vbox->setContentsMargins(0, 0, 0, 0); vbox->addLayout(hbox); QLabel *infoWidgetLabel = new QLabel(info.m_infoText); infoWidgetLabel->setWordWrap(true); infoWidgetLabel->setOpenExternalLinks(true); hbox->addWidget(infoWidgetLabel, 1); if (info.m_detailsWidgetCreator) { if (m_isShowingDetailsWidget) { QWidget *detailsWidget = info.m_detailsWidgetCreator(); vbox->addWidget(detailsWidget); } auto showDetailsButton = new QToolButton; showDetailsButton->setCheckable(true); showDetailsButton->setChecked(m_isShowingDetailsWidget); showDetailsButton->setText(tr("&Show Details")); connect(showDetailsButton, &QToolButton::clicked, [this, vbox, info] (bool) { QWidget *detailsWidget = vbox->count() == 2 ? vbox->itemAt(1)->widget() : nullptr; if (!detailsWidget) { detailsWidget = info.m_detailsWidgetCreator(); vbox->addWidget(detailsWidget); } m_isShowingDetailsWidget = !m_isShowingDetailsWidget; detailsWidget->setVisible(m_isShowingDetailsWidget); }); hbox->addWidget(showDetailsButton); } else { m_isShowingDetailsWidget = false; } if (!info.m_combo.entries.isEmpty()) { auto cb = new QComboBox(); cb->setToolTip(info.m_combo.tooltip); for (const InfoBarEntry::ComboInfo &comboInfo : qAsConst(info.m_combo.entries)) cb->addItem(comboInfo.displayText, comboInfo.data); if (info.m_combo.currentIndex >= 0 && info.m_combo.currentIndex < cb->count()) cb->setCurrentIndex(info.m_combo.currentIndex); connect(cb, &QComboBox::currentIndexChanged, this, [cb, info]() { info.m_combo.callback({cb->currentText(), cb->currentData()}); }, Qt::QueuedConnection); hbox->addWidget(cb); } for (const InfoBarEntry::Button &button : qAsConst(info.m_buttons)) { auto infoWidgetButton = new QToolButton; infoWidgetButton->setText(button.text); infoWidgetButton->setToolTip(button.tooltip); connect(infoWidgetButton, &QAbstractButton::clicked, [button]() { button.callback(); }); hbox->addWidget(infoWidgetButton); } const Id id = info.m_id; QToolButton *infoWidgetSuppressButton = nullptr; if (info.m_globalSuppression == InfoBarEntry::GlobalSuppression::Enabled) { infoWidgetSuppressButton = new QToolButton; infoWidgetSuppressButton->setText(tr("Do Not Show Again")); connect(infoWidgetSuppressButton, &QAbstractButton::clicked, this, [this, id] { m_infoBar->removeInfo(id); InfoBar::globallySuppressInfo(id); }); } QToolButton *infoWidgetCloseButton = nullptr; if (info.m_useCancelButton) { infoWidgetCloseButton = new QToolButton; // need to connect to cancelObjectbefore connecting to cancelButtonClicked, // because the latter removes the button and with it any connect if (info.m_cancelButtonCallBack) connect(infoWidgetCloseButton, &QAbstractButton::clicked, info.m_cancelButtonCallBack); connect(infoWidgetCloseButton, &QAbstractButton::clicked, this, [this, id] { m_infoBar->removeInfo(id); }); } if (info.m_cancelButtonText.isEmpty()) { if (infoWidgetCloseButton) { infoWidgetCloseButton->setAutoRaise(true); infoWidgetCloseButton->setIcon(Icons::CLOSE_FOREGROUND.icon()); infoWidgetCloseButton->setToolTip(tr("Close")); } if (infoWidgetSuppressButton) hbox->addWidget(infoWidgetSuppressButton); if (infoWidgetCloseButton) hbox->addWidget(infoWidgetCloseButton); } else { infoWidgetCloseButton->setText(info.m_cancelButtonText); hbox->addWidget(infoWidgetCloseButton); if (infoWidgetSuppressButton) hbox->addWidget(infoWidgetSuppressButton); } connect(infoWidget, &QObject::destroyed, this, [this, infoWidget] { m_infoWidgets.removeOne(infoWidget); }); m_boxLayout->insertWidget(m_boxIndex, infoWidget); m_infoWidgets << infoWidget; } } } // namespace Utils