diff --git a/src/blackgui/components/logcomponent.cpp b/src/blackgui/components/logcomponent.cpp index 5434fc67b..1bc521e2c 100644 --- a/src/blackgui/components/logcomponent.cpp +++ b/src/blackgui/components/logcomponent.cpp @@ -117,7 +117,7 @@ namespace BlackGui void CLogComponent::appendStatusMessageToConsole(const CStatusMessage &statusMessage) { if (statusMessage.isEmpty()) return; - ui->tep_StatusPageConsole->appendHtml(statusMessage.toHtml()); + ui->tep_StatusPageConsole->appendHtml(statusMessage.toHtml(false)); } void CLogComponent::appendPlainTextToConsole(const QString &text) diff --git a/src/blackgui/guiutility.cpp b/src/blackgui/guiutility.cpp index 09615697d..73a6c3988 100644 --- a/src/blackgui/guiutility.cpp +++ b/src/blackgui/guiutility.cpp @@ -499,6 +499,7 @@ namespace BlackGui QSizeF CGuiUtility::fontMetrics80Chars(bool withRatio) { + // scale is 3.0 on my hires display static const QString s("01234567890123456789012345678901234567890123456789012345678901234567890123456789"); const QFontMetricsF fm = CGuiUtility::currentFontMetricsF(); const qreal scale = withRatio ? CGuiUtility::mainApplicationWidgetPixelRatio() : 1.0; @@ -522,11 +523,12 @@ namespace BlackGui { // 1920/1080: 560/16 256/16 => 530/960 // 3840/2160: 400/10 178/10 => 375/600 + // with ratio we get the physical solution, otherwise logical solution const QSizeF s1 = CGuiUtility::fontMetrics80Chars(withRatio); const QSizeF s2 = CGuiUtility::fontMetricsLazyDog43Chars(withRatio); const QSizeF s = s1 + s2; - const qreal w = s.width() * xCharacters / 123; - const qreal h = s.height() * yCharacters / 2; + const qreal w = s.width() * xCharacters / 123; // 123 chars + const qreal h = s.height() * yCharacters / 2; // 2 lines return QSizeF(w, h); } diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index 0d4e3ccc1..976d39f4e 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -74,7 +74,7 @@ namespace BlackGui { // the underlying model object as summary const CStatusMessage msg(this->at(index)); - return msg.toHtml(); + return msg.toHtml(false); } return CListModelTimestampObjects::data(index, role); } diff --git a/src/blackgui/overlaymessages.cpp b/src/blackgui/overlaymessages.cpp index 453b2ad06..58452b40f 100644 --- a/src/blackgui/overlaymessages.cpp +++ b/src/blackgui/overlaymessages.cpp @@ -54,7 +54,7 @@ namespace BlackGui { this->init(w, h); this->showKillButton(false); - connect(sGui, &CGuiApplication::styleSheetsChanged, this, &COverlayMessages::onStyleSheetsChanged, Qt::QueuedConnection); + if (sGui) { connect(sGui, &CGuiApplication::styleSheetsChanged, this, &COverlayMessages::onStyleSheetsChanged, Qt::QueuedConnection); } connect(ui->pb_Ok, &QPushButton::clicked, this, &COverlayMessages::onOkClicked); connect(ui->pb_Cancel, &QPushButton::clicked, this, &COverlayMessages::onCancelClicked); connect(ui->tb_Kill, &QPushButton::clicked, this, &COverlayMessages::onKillClicked); @@ -126,7 +126,7 @@ namespace BlackGui msgBox.setInformativeText("Do you want to terminate " + sGui->getApplicationNameAndVersion() + "?"); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); - if (QMessageBox::Ok == msgBox.exec()) + if (QMessageBox::Ok == msgBox.exec() && sGui) { sGui->gracefulShutdown(); sGui->exit(); @@ -328,6 +328,28 @@ namespace BlackGui } } + void COverlayMessages::showHTMLMessage(const CStatusMessage &message, int timeOutMs) + { + if (message.isEmpty()) { return; } + if (!sGui || sGui->isShuttingDown()) { return; } + + if (this->hasPendingConfirmation()) + { + // defer message + QPointer myself(this); + m_pendingMessageCalls.push_back([ = ]() + { + if (!myself) { return; } + myself->showHTMLMessage(message, timeOutMs); + }); + return; + } + + this->setModeToHTMLMessage(); + ui->te_HTMLMessage->setText(message.toHtml(true)); + this->display(timeOutMs); + } + void COverlayMessages::showKillButton(bool killButton) { m_hasKillButton = killButton; @@ -355,6 +377,13 @@ namespace BlackGui this->setHeader("Message"); } + void COverlayMessages::setModeToHTMLMessage(bool withKillButton) + { + ui->sw_StatusMessagesComponent->setCurrentWidget(ui->pg_HTMLMessage); + this->showKill(withKillButton); + this->setHeader("Message"); + } + void COverlayMessages::setModeToTextMessage() { ui->sw_StatusMessagesComponent->setCurrentWidget(ui->pg_TextMessage); diff --git a/src/blackgui/overlaymessages.h b/src/blackgui/overlaymessages.h index 21ddcb088..96d1b1f5c 100644 --- a/src/blackgui/overlaymessages.h +++ b/src/blackgui/overlaymessages.h @@ -62,6 +62,9 @@ namespace BlackGui //! Single Message mode void setModeToMessageSmall(bool withKillButton = false); + //! HTML message mode + void setModeToHTMLMessage(bool withKillButton = false); + //! Single Text message mode void setModeToTextMessage(); @@ -110,6 +113,9 @@ namespace BlackGui //! Display one of the supported types void showOverlayVariant(const BlackMisc::CVariant &variant, int timeOutMs = -1); + //! HTML message + void showHTMLMessage(const BlackMisc::CStatusMessage &message, int timeOutMs = -1); + //! Allows to globally enable/disable kill button void showKillButton(bool killButton); diff --git a/src/blackgui/overlaymessages.ui b/src/blackgui/overlaymessages.ui index 6523c47ec..9e167075b 100644 --- a/src/blackgui/overlaymessages.ui +++ b/src/blackgui/overlaymessages.ui @@ -113,7 +113,7 @@ - 5 + 6 @@ -321,6 +321,25 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/src/blackgui/overlaymessagesframe.h b/src/blackgui/overlaymessagesframe.h index 37ecc150d..5428c709f 100644 --- a/src/blackgui/overlaymessagesframe.h +++ b/src/blackgui/overlaymessagesframe.h @@ -163,6 +163,14 @@ namespace BlackGui WIDGET::repaint(); } + //! \copydoc BlackGui::COverlayMessages::showHTMLMessage + void showOverlayHTMLMessage(const BlackMisc::CStatusMessage &message, int timeOutMs = -1) + { + this->initMinimalFrame(); + m_overlayMessages->showHTMLMessage(message, timeOutMs); + WIDGET::repaint(); + } + //! \copydoc BlackGui::COverlayMessages::showOverlayImage void showOverlayInlineTextMessage(Components::TextMessageTab tab) { @@ -206,6 +214,25 @@ namespace BlackGui m_overlayMessages->setGeometry(x, y, w, h); } + //! Init a minimal frame (smaller as the normal one) + void initMinimalFrame() + { + this->initInnerFrame(); + + // get logical resolution + constexpr int MinHeight = 100; + QSizeF s = CGuiUtility::fontMetricsEstimateSize(100, 5); // 2 lines for header + if (s.height() < MinHeight) { s.setHeight(MinHeight); } + + const QSize inner(innerFrameSize()); + const QPoint middle = WIDGET::geometry().center(); + const int w = qMin(inner.width(), qRound(s.width())); + const int h = qMin(inner.height(), qRound(s.height())); + const int x = middle.x() - w / 2; + const int y = qRound(middle.y() - h / m_middleFactor); + m_overlayMessages->setGeometry(x, y, w, h); + } + //! \copydoc QFrame::keyPressEvent virtual void keyPressEvent(QKeyEvent *event) override { diff --git a/src/blackmisc/statusmessage.cpp b/src/blackmisc/statusmessage.cpp index e03215c43..9ab5be736 100644 --- a/src/blackmisc/statusmessage.cpp +++ b/src/blackmisc/statusmessage.cpp @@ -137,8 +137,8 @@ namespace BlackMisc switch (type) { default: - case QtDebugMsg: m_severity = SeverityDebug; break; - case QtInfoMsg: m_severity = SeverityInfo; break; + case QtDebugMsg: m_severity = SeverityDebug; break; + case QtInfoMsg: m_severity = SeverityInfo; break; case QtWarningMsg: m_severity = SeverityWarning; break; case QtCriticalMsg: case QtFatalMsg: @@ -155,10 +155,10 @@ namespace BlackMisc switch (m_severity) { default: - case SeverityDebug: *o_type = QtDebugMsg; break; - case SeverityInfo: *o_type = QtInfoMsg; break; + case SeverityDebug: *o_type = QtDebugMsg; break; + case SeverityInfo: *o_type = QtInfoMsg; break; case SeverityWarning: *o_type = QtWarningMsg; break; - case SeverityError: *o_type = QtCriticalMsg; break; + case SeverityError: *o_type = QtCriticalMsg; break; } } @@ -182,8 +182,8 @@ namespace BlackMisc QString CStatusMessage::getHumanReadablePattern() const { - QStringList patternNames(getHumanReadablePatterns()); - return patternNames.isEmpty() ? "" : patternNames.join(", "); + const QStringList patternNames(getHumanReadablePatterns()); + return patternNames.isEmpty() ? QStringLiteral("") : patternNames.join(", "); } QStringList CStatusMessage::getHumanReadablePatterns() const @@ -199,7 +199,7 @@ namespace BlackMisc QString CStatusMessage::getHumanOrTechnicalCategoriesAsString() const { if (m_categories.isEmpty()) { return ""; } - QString c(getHumanReadablePattern()); + const QString c(getHumanReadablePattern()); return c.isEmpty() ? this->getCategoriesAsString() : c; } @@ -280,14 +280,31 @@ namespace BlackMisc { switch (severity) { - case SeverityDebug: return CIcon::iconByIndex(CIcons::StandardIconUnknown16); // TODO - case SeverityInfo: return CIcon::iconByIndex(CIcons::StandardIconInfo16); + case SeverityDebug: return CIcon::iconByIndex(CIcons::StandardIconUnknown16); // TODO + case SeverityInfo: return CIcon::iconByIndex(CIcons::StandardIconInfo16); case SeverityWarning: return CIcon::iconByIndex(CIcons::StandardIconWarning16); - case SeverityError: return CIcon::iconByIndex(CIcons::StandardIconError16); + case SeverityError: return CIcon::iconByIndex(CIcons::StandardIconError16); default: return CIcon::iconByIndex(CIcons::StandardIconInfo16); } } + const QString &CStatusMessage::convertToIconResource(CStatusMessage::StatusSeverity severity) + { + static const QString d; + static const QString i(":/pastel/icons/pastel/16/infomation.png"); + static const QString w(":/pastel/icons/pastel/16/bullet-error.png"); + static const QString e(":/pastel/icons/pastel/16/close-red.png"); + + switch (severity) + { + case SeverityDebug: return d; + case SeverityInfo: return i; + case SeverityWarning: return w; + case SeverityError: return e; + default: return d; + } + } + CStatusMessage CStatusMessage::fromDatabaseJson(const QJsonObject &json) { QString msgText(json.value("text").toString()); @@ -333,10 +350,10 @@ namespace BlackMisc { switch (severity) { - case SeverityDebug: { static const QString d("debug"); return d; } - case SeverityInfo: { static const QString i("info"); return i; } + case SeverityDebug: { static const QString d("debug"); return d; } + case SeverityInfo: { static const QString i("info"); return i; } case SeverityWarning: { static const QString w("warning"); return w; } - case SeverityError: { static const QString e("error"); return e; } + case SeverityError: { static const QString e("error"); return e; } default: { static const QString x("unknown severity"); @@ -385,7 +402,7 @@ namespace BlackMisc case IndexCategoriesAsString: return CVariant::from(m_categories.toQString()); case IndexCategoriesHumanReadableAsString: return CVariant::from(this->getHumanReadablePattern()); case IndexCategoryHumanReadableOrTechnicalAsString: return CVariant::from(this->getHumanOrTechnicalCategoriesAsString()); - case IndexMessageAsHtml: return CVariant::from(this->toHtml()); + case IndexMessageAsHtml: return CVariant::from(this->toHtml(false)); default: return CValueObject::propertyByIndex(index); } } @@ -428,16 +445,22 @@ namespace BlackMisc return 0; } - QString CStatusMessage::toHtml() const + QString CStatusMessage::toHtml(bool withIcon) const { - static const QString ef(""); + QString img; + if (withIcon) + { + const QString r = convertToIconResource(this->getSeverity()); + if (!r.isEmpty()) { img = QStringLiteral(" ").arg(r); } + } + switch (this->getSeverity()) { - case SeverityWarning: return QStringLiteral("") % this->getMessage() % ef; - case SeverityError: return QStringLiteral("") % this->getMessage() % ef; + case SeverityWarning: return img % QStringLiteral("") % this->getMessage() % QStringLiteral(""); + case SeverityError: return img % QStringLiteral("") % this->getMessage() % QStringLiteral(""); case SeverityDebug: break; default: break; } - return this->getMessage(); + return img % this->getMessage(); } } // ns diff --git a/src/blackmisc/statusmessage.h b/src/blackmisc/statusmessage.h index 022bf38e0..a8f784828 100644 --- a/src/blackmisc/statusmessage.h +++ b/src/blackmisc/statusmessage.h @@ -347,7 +347,7 @@ namespace BlackMisc QString convertToQString(bool i18n = false) const; //! To HTML - QString toHtml() const; + QString toHtml(bool withIcon) const; //! Representing icon static const CIcon &convertToIcon(const CStatusMessage &statusMessage); @@ -355,6 +355,9 @@ namespace BlackMisc //! Representing icon static const CIcon &convertToIcon(CStatusMessage::StatusSeverity severity); + //! Representing icon + static const QString &convertToIconResource(CStatusMessage::StatusSeverity severity); + //! Object from JSON static CStatusMessage fromDatabaseJson(const QJsonObject &json);