Qt: add plaintext log and more log options

This commit is contained in:
Megamouse 2019-12-20 23:29:33 +01:00
parent e83dfedff9
commit e54438d3a7
7 changed files with 251 additions and 174 deletions

View File

@ -134,6 +134,8 @@
}
},
"gui": {
"log_limit": "Sets the maximum amount of blocks that the log can display.\nThis usually equals the number of lines.\nSet 0 in order to remove the limit.",
"tty_limit": "Sets the maximum amount of blocks that the tty can display.\nThis usually equals the number of lines.\nSet 0 in order to remove the limit.",
"configs": "Only useful to developers.\nIf unsure, don't use this option.",
"stylesheets": "Changes the overall look of RPCS3.\nChoose a stylesheet and click Apply to change between styles.",
"show_welcome": "Shows the initial welcome screen upon starting RPCS3.",

View File

@ -200,7 +200,9 @@ namespace gui
const gui_save l_tty = gui_save(logger, "TTY", true);
const gui_save l_level = gui_save(logger, "level", static_cast<uint>(logs::level::success));
const gui_save l_stack = gui_save(logger, "stack", true);
const gui_save l_stack_tty = gui_save(logger, "TTY stack", false);
const gui_save l_stack_tty = gui_save(logger, "TTY_stack", false);
const gui_save l_limit = gui_save(logger, "limit", 1000);
const gui_save l_limit_tty = gui_save(logger, "TTY_limit", 1000);
const gui_save d_splitterState = gui_save(debugger, "splitterState", QByteArray());
const gui_save d_centerPC = gui_save(debugger, "centerPC", false);

View File

@ -1,4 +1,4 @@
#include "log_frame.h"
#include "log_frame.h"
#include "qt_utils.h"
#include "stdafx.h"
#include "rpcs3_version.h"
@ -101,22 +101,27 @@ struct gui_listener : logs::listener
static gui_listener s_gui_listener;
log_frame::log_frame(std::shared_ptr<gui_settings> guiSettings, QWidget *parent)
: custom_dock_widget(tr("Log"), parent), xgui_settings(guiSettings)
: custom_dock_widget(tr("Log"), parent), m_gui_settings(std::move(guiSettings))
{
m_tabWidget = new QTabWidget;
m_tabWidget->setObjectName("tab_widget_log");
m_tabWidget->tabBar()->setObjectName("tab_bar_log");
const int max_block_count_log = m_gui_settings->GetValue(gui::l_limit).toInt();
const int max_block_count_tty = m_gui_settings->GetValue(gui::l_limit_tty).toInt();
m_log = new QTextEdit(m_tabWidget);
m_log->setObjectName("log_frame");
m_tabWidget = new QTabWidget;
m_tabWidget->setObjectName(QStringLiteral("tab_widget_log"));
m_tabWidget->tabBar()->setObjectName(QStringLiteral("tab_bar_log"));
m_log = new QPlainTextEdit(m_tabWidget);
m_log->setObjectName(QStringLiteral("log_frame"));
m_log->setReadOnly(true);
m_log->setContextMenuPolicy(Qt::CustomContextMenu);
m_log->document()->setMaximumBlockCount(max_block_count_log);
m_log->installEventFilter(this);
m_tty = new QTextEdit(m_tabWidget);
m_tty->setObjectName("tty_frame");
m_tty = new QPlainTextEdit(m_tabWidget);
m_tty->setObjectName(QStringLiteral("tty_frame"));
m_tty->setReadOnly(true);
m_tty->setContextMenuPolicy(Qt::CustomContextMenu);
m_tty->document()->setMaximumBlockCount(max_block_count_tty);
m_tty->installEventFilter(this);
m_tty_input = new QLineEdit();
@ -216,21 +221,29 @@ void log_frame::CreateAndConnectActions()
connect(act, &QAction::triggered, [this, logLevel]()
{
s_gui_listener.enabled = std::max(logLevel, logs::level::fatal);
xgui_settings->SetValue(gui::l_level, static_cast<uint>(logLevel));
m_gui_settings->SetValue(gui::l_level, static_cast<uint>(logLevel));
});
};
m_clearAct = new QAction(tr("Clear"), this);
connect(m_clearAct, &QAction::triggered, m_log, &QTextEdit::clear);
connect(m_clearAct, &QAction::triggered, [this]()
{
m_old_log_text = "";
m_log->clear();
});
m_clearTTYAct = new QAction(tr("Clear"), this);
connect(m_clearTTYAct, &QAction::triggered, m_tty, &QTextEdit::clear);
connect(m_clearTTYAct, &QAction::triggered, [this]()
{
m_old_tty_text = "";
m_tty->clear();
});
m_stackAct_tty = new QAction(tr("Stack Mode (TTY)"), this);
m_stackAct_tty->setCheckable(true);
connect(m_stackAct_tty, &QAction::toggled, xgui_settings.get(), [=](bool checked)
connect(m_stackAct_tty, &QAction::toggled, [this](bool checked)
{
xgui_settings->SetValue(gui::l_stack_tty, checked);
m_gui_settings->SetValue(gui::l_stack_tty, checked);
m_stack_tty = checked;
});
@ -271,17 +284,17 @@ void log_frame::CreateAndConnectActions()
m_stackAct_log = new QAction(tr("Stack Mode (Log)"), this);
m_stackAct_log->setCheckable(true);
connect(m_stackAct_log, &QAction::toggled, xgui_settings.get(), [=](bool checked)
connect(m_stackAct_log, &QAction::toggled, [this](bool checked)
{
xgui_settings->SetValue(gui::l_stack, checked);
m_gui_settings->SetValue(gui::l_stack, checked);
m_stack_log = checked;
});
m_TTYAct = new QAction(tr("TTY"), this);
m_TTYAct->setCheckable(true);
connect(m_TTYAct, &QAction::triggered, xgui_settings.get(), [=](bool checked)
connect(m_TTYAct, &QAction::triggered, [this](bool checked)
{
xgui_settings->SetValue(gui::l_tty, checked);
m_gui_settings->SetValue(gui::l_tty, checked);
});
l_initAct(m_nothingAct, logs::level::fatal);
@ -293,7 +306,7 @@ void log_frame::CreateAndConnectActions()
l_initAct(m_noticeAct, logs::level::notice);
l_initAct(m_traceAct, logs::level::trace);
connect(m_log, &QWidget::customContextMenuRequested, [=](const QPoint& pos)
connect(m_log, &QWidget::customContextMenuRequested, [this](const QPoint& pos)
{
QMenu* menu = m_log->createStandardContextMenu();
menu->addAction(m_clearAct);
@ -306,7 +319,7 @@ void log_frame::CreateAndConnectActions()
menu->exec(m_log->viewport()->mapToGlobal(pos));
});
connect(m_tty, &QWidget::customContextMenuRequested, [=](const QPoint& pos)
connect(m_tty, &QWidget::customContextMenuRequested, [this](const QPoint& pos)
{
QMenu* menu = m_tty->createStandardContextMenu();
menu->addAction(m_clearTTYAct);
@ -368,12 +381,22 @@ void log_frame::CreateAndConnectActions()
void log_frame::LoadSettings()
{
SetLogLevel(xgui_settings->GetLogLevel());
SetTTYLogging(xgui_settings->GetValue(gui::l_tty).toBool());
m_stack_log = xgui_settings->GetValue(gui::l_stack).toBool();
m_stack_tty = xgui_settings->GetValue(gui::l_stack_tty).toBool();
SetLogLevel(m_gui_settings->GetLogLevel());
SetTTYLogging(m_gui_settings->GetValue(gui::l_tty).toBool());
m_stack_log = m_gui_settings->GetValue(gui::l_stack).toBool();
m_stack_tty = m_gui_settings->GetValue(gui::l_stack_tty).toBool();
m_stackAct_log->setChecked(m_stack_log);
m_stackAct_tty->setChecked(m_stack_tty);
if (m_log)
{
m_log->document()->setMaximumBlockCount(m_gui_settings->GetValue(gui::l_limit).toInt());
}
if (m_tty)
{
m_tty->document()->setMaximumBlockCount(m_gui_settings->GetValue(gui::l_limit_tty).toInt());
}
}
void log_frame::RepaintTextColors()
@ -396,56 +419,12 @@ void log_frame::RepaintTextColors()
m_color_stack = gui::utils::get_label_color("log_stack");
// Repaint TTY with new colors
m_tty->setTextColor(gui::utils::get_label_color("tty_text"));
QTextCursor tty_cursor = m_tty->textCursor();
QTextCharFormat text_format = tty_cursor.charFormat();
text_format.setForeground(gui::utils::get_label_color("tty_text"));
m_tty->setTextCursor(tty_cursor);
// Repaint log with new colors
QTextCursor text_cursor{ m_log->document() };
text_cursor.movePosition(QTextCursor::Start);
// Go through each line
while (!text_cursor.atEnd())
{
// Jump to the next line, unless this is the first one
if (!text_cursor.atStart())
text_cursor.movePosition(QTextCursor::NextBlock);
// Go through each word in the current line
while (!text_cursor.atBlockEnd())
{
// Remove old selection and select a new character (NextWord has proven to be glitchy here)
text_cursor.movePosition(QTextCursor::NoMove);
text_cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
// Skip if no color needed
if (text_cursor.selectedText() == " ")
continue;
// Get current chars color
QTextCharFormat text_format = text_cursor.charFormat();
QColor col = text_format.foreground().color();
// Get the new color for this word
if (col == old_color_stack)
{
text_format.setForeground(m_color_stack);
}
else
{
for (int i = 0; i < old_color.count(); i++)
{
if (col == old_color[i])
{
text_format.setForeground(m_color[i]);
break;
}
}
}
// Reinsert the same text with the new color
text_cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
text_cursor.insertText(text_cursor.selectedText(), text_format);
}
}
// TODO: Repaint log with new colors
}
void log_frame::UpdateUI()
@ -467,54 +446,52 @@ void log_frame::UpdateUI()
if (buf.size() && m_TTYAct->isChecked())
{
// save old scroll bar state
QScrollBar *sb = m_tty->verticalScrollBar();
const int sb_pos = sb->value();
const bool is_max = sb_pos == sb->maximum();
// save old selection
QTextCursor text_cursor{m_tty->document()};
const int sel_pos = text_cursor.position();
int sel_start = text_cursor.selectionStart();
int sel_end = text_cursor.selectionEnd();
// clear selection or else it will get colorized as well
text_cursor.clearSelection();
std::stringstream buf_stream;
buf_stream.str(buf);
std::string buf_line;
while (std::getline(buf_stream, buf_line))
{
QString suffix;
QString tty_text = QString::fromStdString(buf_line);
bool isSame = tty_text == m_old_tty_text;
// save old scroll bar state
QScrollBar* sb = m_tty->verticalScrollBar();
const int sb_pos = sb->value();
const bool is_max = sb_pos == sb->maximum();
// save old selection
QTextCursor text_cursor{ m_tty->document() };
const int sel_pos = text_cursor.position();
int sel_start = text_cursor.selectionStart();
int sel_end = text_cursor.selectionEnd();
// clear selection or else it will get colorized as well
text_cursor.clearSelection();
const QString tty_text = QString::fromStdString(buf_line);
// create counter suffix and remove recurring line if needed
if (m_stack_tty)
{
if (isSame)
text_cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (tty_text == m_old_tty_text)
{
m_tty_counter++;
suffix = QString(" x%1").arg(m_tty_counter);
m_tty->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
m_tty->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
m_tty->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
m_tty->textCursor().removeSelectedText();
m_tty->textCursor().deletePreviousChar();
text_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
text_cursor.insertText(tty_text % QStringLiteral(" x") % QString::number(++m_tty_counter));
}
else
{
m_tty_counter = 1;
m_old_tty_text = tty_text;
// write text to the end
m_tty->setTextCursor(text_cursor);
m_tty->appendPlainText(tty_text);
}
}
// write text to the end
m_tty->append(tty_text);
// add counter suffix if needed
if (isSame && m_stack_tty)
else
{
m_tty->insertPlainText(suffix);
// write text to the end
m_tty->setTextCursor(text_cursor);
m_tty->appendPlainText(tty_text);
}
// if we mark text from right to left we need to swap sides (start is always smaller than end)
@ -537,6 +514,11 @@ void log_frame::UpdateUI()
if (steady_clock::now() >= start + 4ms || buf.empty()) break;
}
const auto font_start_tag = [](const QColor& color) -> const QString { return QStringLiteral("<font color = \"") % color.name() % QStringLiteral("\">"); };
const QString font_start_tag_stack = "<font color = \"" % m_color_stack.name() % "\">";
const QString font_end_tag = QStringLiteral("</font>");
const QString br_tag = QStringLiteral("<br/>");
// Check main logs
while (auto* packet = s_gui_listener.get())
{
@ -547,13 +529,13 @@ void log_frame::UpdateUI()
switch (packet->sev)
{
case logs::level::always: break;
case logs::level::fatal: text = "F "; break;
case logs::level::error: text = "E "; break;
case logs::level::todo: text = "U "; break;
case logs::level::success: text = "S "; break;
case logs::level::warning: text = "W "; break;
case logs::level::notice: text = "! "; break;
case logs::level::trace: text = "T "; break;
case logs::level::fatal: text = QStringLiteral("F "); break;
case logs::level::error: text = QStringLiteral("E "); break;
case logs::level::todo: text = QStringLiteral("U "); break;
case logs::level::success: text = QStringLiteral("S "); break;
case logs::level::warning: text = QStringLiteral("W "); break;
case logs::level::notice: text = QStringLiteral("! "); break;
case logs::level::trace: text = QStringLiteral("T "); break;
default: continue;
}
@ -561,53 +543,44 @@ void log_frame::UpdateUI()
text += qstr(packet->msg);
// save old log state
QScrollBar *sb = m_log->verticalScrollBar();
bool isMax = sb->value() == sb->maximum();
int sb_pos = sb->value();
int sel_pos = m_log->textCursor().position();
int sel_start = m_log->textCursor().selectionStart();
int sel_end = m_log->textCursor().selectionEnd();
QScrollBar* sb = m_log->verticalScrollBar();
const bool isMax = sb->value() == sb->maximum();
const int sb_pos = sb->value();
QTextCursor text_cursor = m_log->textCursor();
const int sel_pos = text_cursor.position();
int sel_start = text_cursor.selectionStart();
int sel_end = text_cursor.selectionEnd();
// clear selection or else it will get colorized as well
QTextCursor c = m_log->textCursor();
c.clearSelection();
m_log->setTextCursor(c);
text_cursor.clearSelection();
// remove the new line because Qt's append adds a new line already.
text.chop(1);
QString suffix;
bool isSame = text == m_old_log_text;
// create counter suffix and remove recurring line if needed
if (m_stack_log)
{
if (isSame)
// add counter suffix if needed
if (text == m_old_log_text)
{
m_log_counter++;
suffix = QString(" x%1").arg(m_log_counter);
m_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
m_log->moveCursor(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
m_log->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor);
m_log->textCursor().removeSelectedText();
m_log->textCursor().deletePreviousChar();
text_cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
text_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
text_cursor.insertHtml(font_start_tag(m_color[static_cast<int>(packet->sev)]) % text.replace("\n", br_tag) % font_start_tag_stack % QStringLiteral(" x") % QString::number(++m_log_counter) % font_end_tag % font_end_tag);
}
else
{
m_log_counter = 1;
m_old_log_text = text;
m_log->setTextCursor(text_cursor);
m_log->appendHtml(font_start_tag(m_color[static_cast<int>(packet->sev)]) % text.replace("\n", br_tag) % font_end_tag);
}
}
// add actual log message
m_log->setTextColor(m_color[static_cast<int>(packet->sev)]);
m_log->append(text);
// add counter suffix if needed
if (m_stack_log && isSame)
else
{
m_log->setTextColor(m_color_stack);
m_log->insertPlainText(suffix);
m_log->setTextCursor(text_cursor);
m_log->appendHtml(font_start_tag(m_color[static_cast<int>(packet->sev)]) % text.replace("\n", br_tag) % font_end_tag);
}
// if we mark text from right to left we need to swap sides (start is always smaller than end)
@ -617,9 +590,9 @@ void log_frame::UpdateUI()
}
// reset old text cursor and selection
c.setPosition(sel_start);
c.setPosition(sel_end, QTextCursor::KeepAnchor);
m_log->setTextCursor(c);
text_cursor.setPosition(sel_start);
text_cursor.setPosition(sel_end, QTextCursor::KeepAnchor);
m_log->setTextCursor(text_cursor);
// set scrollbar to max means auto-scroll
sb->setValue(isMax ? sb->maximum() : sb_pos);

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "Utilities/File.h"
#include "Utilities/Log.h"
@ -10,7 +10,7 @@
#include <memory>
#include <QTabWidget>
#include <QTextEdit>
#include <QPlainTextEdit>
#include <QActionGroup>
#include <QTimer>
#include <QKeyEvent>
@ -22,12 +22,13 @@ class log_frame : public custom_dock_widget
public:
explicit log_frame(std::shared_ptr<gui_settings> guiSettings, QWidget *parent = nullptr);
/** Loads from settings. Public so that main_window can call this easily. */
void LoadSettings();
/** Repaint log colors after new stylesheet was applied */
void RepaintTextColors();
public Q_SLOTS:
/** Loads from settings. Public so that main_window can call this easily. */
void LoadSettings();
Q_SIGNALS:
void LogFrameClosed();
protected:
@ -42,45 +43,45 @@ private:
void CreateAndConnectActions();
QTabWidget* m_tabWidget;
QTabWidget* m_tabWidget = nullptr;
std::unique_ptr<find_dialog> m_find_dialog;
QList<QColor> m_color;
QColor m_color_stack;
QTextEdit* m_log;
QPlainTextEdit* m_log = nullptr;
QString m_old_log_text;
QString m_old_tty_text;
ullong m_log_counter;
ullong m_tty_counter;
bool m_stack_log;
bool m_stack_tty;
ullong m_log_counter{};
ullong m_tty_counter{};
bool m_stack_log{};
bool m_stack_tty{};
fs::file m_tty_file;
QWidget* m_tty_container;
QTextEdit* m_tty;
QLineEdit* m_tty_input;
QWidget* m_tty_container = nullptr;
QPlainTextEdit* m_tty = nullptr;
QLineEdit* m_tty_input = nullptr;
int m_tty_channel = -1;
QAction* m_clearAct;
QAction* m_clearTTYAct;
QAction* m_clearAct = nullptr;
QAction* m_clearTTYAct = nullptr;
QActionGroup* m_logLevels;
QAction* m_nothingAct;
QAction* m_fatalAct;
QAction* m_errorAct;
QAction* m_todoAct;
QAction* m_successAct;
QAction* m_warningAct;
QAction* m_noticeAct;
QAction* m_traceAct;
QActionGroup* m_logLevels = nullptr;
QAction* m_nothingAct = nullptr;
QAction* m_fatalAct = nullptr;
QAction* m_errorAct = nullptr;
QAction* m_todoAct = nullptr;
QAction* m_successAct = nullptr;
QAction* m_warningAct = nullptr;
QAction* m_noticeAct = nullptr;
QAction* m_traceAct = nullptr;
QAction* m_stackAct_log;
QAction* m_stackAct_tty;
QAction* m_stackAct_log = nullptr;
QAction* m_stackAct_tty = nullptr;
QAction* m_TTYAct;
QAction* m_TTYAct = nullptr;
QActionGroup* m_tty_channel_acts;
QActionGroup* m_tty_channel_acts = nullptr;
std::shared_ptr<gui_settings> xgui_settings;
std::shared_ptr<gui_settings> m_gui_settings;
};

View File

@ -1256,6 +1256,7 @@ void main_window::CreateConnects()
connect(&dlg, &settings_dialog::GuiStylesheetRequest, this, &main_window::RequestGlobalStylesheetChange);
connect(&dlg, &settings_dialog::GuiRepaintRequest, this, &main_window::RepaintGui);
connect(&dlg, &settings_dialog::accepted, this, &main_window::NotifyEmuSettingsChange);
connect(&dlg, &settings_dialog::accepted, m_logFrame, &log_frame::LoadSettings);
dlg.exec();
};

View File

@ -1338,6 +1338,22 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
m_discord_state = ui->discordState->text();
});
// Log and TTY:
SubscribeTooltip(ui->log_limit, json_gui["log_limit"].toString());
SubscribeTooltip(ui->tty_limit, json_gui["tty_limit"].toString());
ui->spinbox_log_limit->setValue(xgui_settings->GetValue(gui::l_limit).toInt());
connect(ui->spinbox_log_limit, &QSpinBox::editingFinished, [=]()
{
xgui_settings->SetValue(gui::l_limit, ui->spinbox_log_limit->value());
});
ui->spinbox_tty_limit->setValue(xgui_settings->GetValue(gui::l_limit_tty).toInt());
connect(ui->spinbox_tty_limit, &QSpinBox::editingFinished, [=]()
{
xgui_settings->SetValue(gui::l_limit_tty, ui->spinbox_tty_limit->value());
});
// colorize preview icons
auto addColoredIcon = [&](QPushButton *button, const QColor& color, const QIcon& icon = QIcon(), const QColor& iconColor = QColor())
{

View File

@ -2518,6 +2518,88 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_log">
<property name="title">
<string>Log</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_88">
<item>
<widget class="QWidget" name="log_limit" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="layout_log_limit">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_log_limit">
<property name="text">
<string>Maximum log blocks</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinbox_log_limit">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="tty_limit" native="true">
<layout class="QVBoxLayout" name="layout_tty_limit">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_tty_limit">
<property name="text">
<string>Maximum TTY blocks</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinbox_tty_limit">
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>