diff --git a/PreCompiledHeaders.cmake b/PreCompiledHeaders.cmake index e0a4d70d639d15cec9971433e9744d44b9a3af31..b04fef6941a16f4b677b8d7bbb1604ff09ff989b 100644 --- a/PreCompiledHeaders.cmake +++ b/PreCompiledHeaders.cmake @@ -139,6 +139,7 @@ set(QT_WIDGET_INC <QFileIconProvider> <QFontDatabase> <QTextCodec> + <QListWidget> ) set(Vivy_PRECOMPILED_INC PRIVATE diff --git a/src/Lib/Log.hh b/src/Lib/Log.hh index 428fb35879cc48849da4d9cd91cea82fcd0e3b60..063187a9ea52e5e79fb8a84d0f2fc99c59c4c557 100644 --- a/src/Lib/Log.hh +++ b/src/Lib/Log.hh @@ -197,6 +197,13 @@ public: return dispatch; } + template <Derived<LogSinkDispatcher> DispatcherType, typename... Args> + DispatcherType *newUnmanagedDispatcher(Args &&...args) noexcept + { + DispatcherType *dispatch = new DispatcherType(std::forward<Args>(args)...); + return dispatch; + } + std::shared_ptr<Logger> newLogger(const StringType auto &category) noexcept { std::shared_ptr<Logger> logger = std::make_shared<Logger>(this, category); diff --git a/src/UI/LogConsole.cc b/src/UI/LogConsole.cc new file mode 100644 index 0000000000000000000000000000000000000000..7428a1a6a569d0c48b3f3994a5b19d82c338fd87 --- /dev/null +++ b/src/UI/LogConsole.cc @@ -0,0 +1,55 @@ +#include "LogConsole.hh" + +using namespace Vivy; + +ConsoleLogSinkDispatcher::ConsoleLogSinkDispatcher(QWidget *parent) noexcept + : LogSinkDispatcher(std::string_view{ "console" }) + , QListWidget(parent) +{ + setSortingEnabled(false); + setSelectionMode(QAbstractItemView::NoSelection); + + const QString style = QStringLiteral("* { font-family: \"FiraCode\"; font-size: 8pt; }"); + setStyleSheet(style); +} + +void +ConsoleLogSinkDispatcher::setMessageCountLimit(int limit) +{ + if (limit <= 0) + throw std::logic_error("Can't pass a negative count!"); + messageLimit = limit; +} + +void +ConsoleLogSinkDispatcher::handleLogMessage(const std::string_view cat, + const LogMessage &msg) noexcept +{ + while (count() >= messageLimit) { + QListWidgetItem *itemWidget = item(0); + if (itemWidget != nullptr) + removeItemWidget(itemWidget); + } + + QString label; + + // Magic number to reduce early allocations + label.reserve(std::max(static_cast<int>(cat.size()) * 2, 10)); + + label.append('['); + for (const char c : cat) + label.append(c); + label.append("] "); + + for (const char c : LogLevel::toStdStringView(msg.getHeader().severity)) + label.append(c); + label.append(" -> "); + + // Add the content of the message + label.reserve(static_cast<int>(1 + label.size() + + std::max(10, static_cast<int>(msg.getTextBuffer().size())))); + for (const char c : msg.getTextBuffer()) + label.append(c); + + addItem(label); +} diff --git a/src/UI/LogConsole.hh b/src/UI/LogConsole.hh new file mode 100644 index 0000000000000000000000000000000000000000..2525f10e86a7ba936e13566bff1efe2673652005 --- /dev/null +++ b/src/UI/LogConsole.hh @@ -0,0 +1,19 @@ +#pragma once + +#include "Utils.hh" +#include "../Lib/Log.hh" + +namespace Vivy +{ +class ConsoleLogSinkDispatcher final : public LogSinkDispatcher, public QListWidget { + VIVY_UNMOVABLE_OBJECT(ConsoleLogSinkDispatcher) + + int messageLimit{ 1'000 }; + +public: + explicit ConsoleLogSinkDispatcher(QWidget *parent = nullptr) noexcept; + void handleLogMessage(const std::string_view, const LogMessage &) noexcept override; + + void setMessageCountLimit(int limit); +}; +} diff --git a/src/UI/MainWindow.cc b/src/UI/MainWindow.cc index b0f5f030dd731b6b1147400dec82d487bde3fa15..83b281bd1f27ca84b690e3029b6d4ea607574a6e 100644 --- a/src/UI/MainWindow.cc +++ b/src/UI/MainWindow.cc @@ -152,14 +152,11 @@ MainWindow::MainWindow() noexcept enableConnection(loadSubDocumentVideoAct, enableLoadSubOnDocument); enableConnection(loadSubDocumentAudioAct, enableLoadSubOnDocument); - // Main window has finished its construction statusBar()->showMessage("QSimulate has started"); - // Minimal size... setMinimumHeight(400); setMinimumWidth(600); - - // Always a new empty document + setUnifiedTitleAndToolBarOnMac(true); newDocument(); } diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc index 286bd28edcb979c67c99e80725c31594df765c64..de244c0a3cffb4b231736bcdef17e8578990c96f 100644 --- a/src/VivyApplication.cc +++ b/src/VivyApplication.cc @@ -1,5 +1,6 @@ #include "VivyApplication.hh" #include "UI/MainWindow.hh" +#include "UI/DockWidgetTitleBar.hh" #include "Lib/Document/VivyDocumentStore.hh" #include "Lib/Script/ScriptStore.hh" @@ -40,8 +41,6 @@ VivyApplication::setTheme(Theme theme) noexcept setStyleSheet(stylesheetStream.readAll()); logInfo() << "Theme set using " << VIVY_LOG_QUOTED(sheet); } - - flushLogSink(); } int @@ -82,14 +81,22 @@ VivyApplication::exec() noexcept } case ApplicationType::GUI: { - // Show the main window - mainWindowPtr = std::make_shared<MainWindow>(); + // Show the main window, also set up the log console + mainWindowPtr = std::make_shared<MainWindow>(); + QDockWidget *logConsoleDock = new QDockWidget("Console", mainWindowPtr.get()); + std::shared_ptr<ConsoleLogSinkDispatcher> consoleLogSinkDispatcher = + logSink->newDispatcher<ConsoleLogSinkDispatcher>(nullptr); + + DockWidgetTitleBar::addToDock(logConsoleDock); + logConsoleDock->setAllowedAreas(Qt::BottomDockWidgetArea); + logConsoleDock->setFeatures(QDockWidget::DockWidgetMovable); + logConsoleDock->setWidget(consoleLogSinkDispatcher.get()); + mainWindowPtr->addDockWidget(Qt::BottomDockWidgetArea, logConsoleDock); mainWindowPtr->show(); logInfo() << "Entering the main event loop"; // Main loop - flushLogSink(); return QApplication::exec(); } } diff --git a/src/VivyApplication.hh b/src/VivyApplication.hh index 18b9cacec08da43437758cdca2e0662551ac4a52..7317c7ea752bfea182e02d36a48bcf82281dd74a 100644 --- a/src/VivyApplication.hh +++ b/src/VivyApplication.hh @@ -40,6 +40,7 @@ #endif #include "Lib/Log.hh" +#include "UI/LogConsole.hh" namespace Vivy {