diff --git a/rpcs3/Json/tooltips.json b/rpcs3/Json/tooltips.json index 62a0135fe2..862b365313 100644 --- a/rpcs3/Json/tooltips.json +++ b/rpcs3/Json/tooltips.json @@ -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.", diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 26a45f1023..f30d1550ea 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -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(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); diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index b921ae842e..b26d37c66e 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -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 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(logLevel)); + m_gui_settings->SetValue(gui::l_level, static_cast(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(""); }; + const QString font_start_tag_stack = ""; + const QString font_end_tag = QStringLiteral(""); + const QString br_tag = QStringLiteral("
"); + // 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(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(packet->sev)]) % text.replace("\n", br_tag) % font_end_tag); } } - - // add actual log message - m_log->setTextColor(m_color[static_cast(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(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); diff --git a/rpcs3/rpcs3qt/log_frame.h b/rpcs3/rpcs3qt/log_frame.h index 3fd88d3429..0d5719afaa 100644 --- a/rpcs3/rpcs3qt/log_frame.h +++ b/rpcs3/rpcs3qt/log_frame.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Utilities/File.h" #include "Utilities/Log.h" @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -22,12 +22,13 @@ class log_frame : public custom_dock_widget public: explicit log_frame(std::shared_ptr 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 m_find_dialog; QList 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 xgui_settings; + std::shared_ptr m_gui_settings; }; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index f941a7a2a5..90a5c79508 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -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(); }; diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 0573454138..139a060e55 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1338,6 +1338,22 @@ settings_dialog::settings_dialog(std::shared_ptr 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()) { diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 1b3cce9453..dcd1483426 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2518,6 +2518,88 @@ + + + + Log + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Maximum log blocks + + + + + + + 0 + + + 999999999 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Maximum TTY blocks + + + + + + + 999999999 + + + + + + + + +