This commit includes two big changes, alongside other smaller tweaks. 1) Update the internal QTextDocument of the text component directly 2) Use QPlainTextEdit, which supports HTML & is designed for long text 3) Use QString's arg function for formatting strings Fix 1 significantly improves realtime performance when adding lines individually, to the point that the UI no longer freezes if the viewer is open and the log is being spammed. It also improves initial launch speed when there's a large amount of text already in the file. Reference: https://stackoverflow.com/a/54501760/2763321 Fix 2 completely eliminates delay when opening the viewer, regardless of how many lines are already in the log file. For a standard log after OBS launch, this cuts opening time from about 2 seconds to half a second. For anything longer than 1,000 lines, the UI no longer freezes, and the viewer (& its contents) open within half a second. Reference: https://stackoverflow.com/a/17466240/2763321
179 lines
4.3 KiB
C++
179 lines
4.3 KiB
C++
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QScrollBar>
|
|
#include <QFont>
|
|
#include <QFontDatabase>
|
|
#include <QPushButton>
|
|
#include <QCheckBox>
|
|
#include <QLayout>
|
|
#include <QDesktopServices>
|
|
#include <string>
|
|
|
|
#include "log-viewer.hpp"
|
|
#include "qt-wrappers.hpp"
|
|
|
|
OBSLogViewer::OBSLogViewer(QWidget *parent) : QDialog(parent)
|
|
{
|
|
setWindowFlags(windowFlags() & Qt::WindowMaximizeButtonHint &
|
|
~Qt::WindowContextHelpButtonHint);
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout();
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
const QFont fixedFont =
|
|
QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
|
|
|
textArea = new QPlainTextEdit();
|
|
textArea->setReadOnly(true);
|
|
textArea->setFont(fixedFont);
|
|
|
|
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
|
QPushButton *clearButton = new QPushButton(QTStr("Clear"));
|
|
connect(clearButton, &QPushButton::clicked, this,
|
|
&OBSLogViewer::ClearText);
|
|
QPushButton *openButton = new QPushButton(QTStr("OpenFile"));
|
|
connect(openButton, &QPushButton::clicked, this,
|
|
&OBSLogViewer::OpenFile);
|
|
QPushButton *closeButton = new QPushButton(QTStr("Close"));
|
|
connect(closeButton, &QPushButton::clicked, this, &QDialog::close);
|
|
|
|
bool showLogViewerOnStartup = config_get_bool(
|
|
App()->GlobalConfig(), "LogViewer", "ShowLogStartup");
|
|
|
|
QCheckBox *showStartup = new QCheckBox(QTStr("ShowOnStartup"));
|
|
showStartup->setChecked(showLogViewerOnStartup);
|
|
connect(showStartup, SIGNAL(toggled(bool)), this,
|
|
SLOT(ToggleShowStartup(bool)));
|
|
|
|
buttonLayout->addSpacing(10);
|
|
buttonLayout->addWidget(showStartup);
|
|
buttonLayout->addStretch();
|
|
buttonLayout->addWidget(openButton);
|
|
buttonLayout->addWidget(clearButton);
|
|
buttonLayout->addWidget(closeButton);
|
|
buttonLayout->addSpacing(10);
|
|
buttonLayout->setContentsMargins(0, 0, 0, 4);
|
|
|
|
layout->addWidget(textArea);
|
|
layout->addLayout(buttonLayout);
|
|
setLayout(layout);
|
|
|
|
setWindowTitle(QTStr("LogViewer"));
|
|
resize(800, 300);
|
|
|
|
const char *geom = config_get_string(App()->GlobalConfig(), "LogViewer",
|
|
"geometry");
|
|
|
|
if (geom != nullptr) {
|
|
QByteArray ba = QByteArray::fromBase64(QByteArray(geom));
|
|
restoreGeometry(ba);
|
|
}
|
|
|
|
InitLog();
|
|
}
|
|
|
|
OBSLogViewer::~OBSLogViewer()
|
|
{
|
|
config_set_string(App()->GlobalConfig(), "LogViewer", "geometry",
|
|
saveGeometry().toBase64().constData());
|
|
}
|
|
|
|
void OBSLogViewer::ToggleShowStartup(bool checked)
|
|
{
|
|
config_set_bool(App()->GlobalConfig(), "LogViewer", "ShowLogStartup",
|
|
checked);
|
|
}
|
|
|
|
extern QPointer<OBSLogViewer> obsLogViewer;
|
|
|
|
void OBSLogViewer::InitLog()
|
|
{
|
|
char logDir[512];
|
|
std::string path;
|
|
|
|
if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) {
|
|
path += logDir;
|
|
path += "/";
|
|
path += App()->GetCurrentLog();
|
|
}
|
|
|
|
QFile file(QT_UTF8(path.c_str()));
|
|
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QTextStream in(&file);
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
in.setCodec("UTF-8");
|
|
#endif
|
|
|
|
QTextDocument *doc = textArea->document();
|
|
QTextCursor cursor(doc);
|
|
cursor.movePosition(QTextCursor::End);
|
|
cursor.beginEditBlock();
|
|
while (!in.atEnd()) {
|
|
QString line = in.readLine();
|
|
cursor.insertHtml(line);
|
|
cursor.insertBlock();
|
|
}
|
|
cursor.endEditBlock();
|
|
|
|
file.close();
|
|
}
|
|
QScrollBar *scroll = textArea->verticalScrollBar();
|
|
scroll->setValue(scroll->maximum());
|
|
|
|
obsLogViewer = this;
|
|
}
|
|
|
|
void OBSLogViewer::AddLine(int type, const QString &str)
|
|
{
|
|
QString msg = str.toHtmlEscaped();
|
|
|
|
switch (type) {
|
|
case LOG_WARNING:
|
|
msg = QString("<font color=\"#c08000\">%1</font>").arg(msg);
|
|
break;
|
|
case LOG_ERROR:
|
|
msg = QString("<font color=\"#c00000\">%1</font>").arg(msg);
|
|
break;
|
|
}
|
|
|
|
QScrollBar *scroll = textArea->verticalScrollBar();
|
|
bool bottomScrolled = scroll->value() >= scroll->maximum() - 10;
|
|
|
|
if (bottomScrolled)
|
|
scroll->setValue(scroll->maximum());
|
|
|
|
QTextDocument *doc = textArea->document();
|
|
QTextCursor cursor(doc);
|
|
cursor.movePosition(QTextCursor::End);
|
|
cursor.beginEditBlock();
|
|
cursor.insertHtml(msg);
|
|
cursor.insertBlock();
|
|
cursor.endEditBlock();
|
|
|
|
if (bottomScrolled)
|
|
scroll->setValue(scroll->maximum());
|
|
}
|
|
|
|
void OBSLogViewer::ClearText()
|
|
{
|
|
textArea->clear();
|
|
}
|
|
|
|
void OBSLogViewer::OpenFile()
|
|
{
|
|
char logDir[512];
|
|
if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0)
|
|
return;
|
|
|
|
const char *log = App()->GetCurrentLog();
|
|
|
|
std::string path = logDir;
|
|
path += "/";
|
|
path += log;
|
|
|
|
QUrl url = QUrl::fromLocalFile(QT_UTF8(path.c_str()));
|
|
QDesktopServices::openUrl(url);
|
|
}
|