@@ -0,0 +1,209 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "DolphinQt2/Config/LogWidget.h"

#include <QCheckBox>
#include <QComboBox>
#include <QFont>
#include <QFontDatabase>
#include <QGroupBox>
#include <QPushButton>
#include <QScrollBar>
#include <QSettings>
#include <QTextEdit>
#include <QTimer>
#include <QVBoxLayout>

#include "Common/FileUtil.h"
#include "Core/ConfigManager.h"
#include "DolphinQt2/Settings.h"

// Delay in ms between calls of UpdateLog()
constexpr int UPDATE_LOG_DELAY = 100;
// Maximum lines to process at a time
constexpr int MAX_LOG_LINES = 200;
// Timestamp length
constexpr int TIMESTAMP_LENGTH = 10;

LogWidget::LogWidget(QWidget* parent) : QDockWidget(parent), m_timer(new QTimer(this))
{
setWindowTitle(tr("Log"));
setHidden(!Settings::Instance().IsLogVisible());
setAllowedAreas(Qt::AllDockWidgetAreas);

CreateWidgets();
LoadSettings();

ConnectWidgets();

connect(m_timer, &QTimer::timeout, this, &LogWidget::UpdateLog);
m_timer->start(UPDATE_LOG_DELAY);

LogManager::GetInstance()->RegisterListener(LogListener::LOG_WINDOW_LISTENER, this);
}

LogWidget::~LogWidget()
{
SaveSettings();

LogManager::GetInstance()->RegisterListener(LogListener::LOG_WINDOW_LISTENER, nullptr);
}

void LogWidget::UpdateLog()
{
std::lock_guard<std::mutex> lock(m_log_mutex);

if (m_log_queue.empty())
return;

auto* vscroll = m_log_text->verticalScrollBar();
auto* hscroll = m_log_text->horizontalScrollBar();

// If the vertical scrollbar is within 50 units of the maximum value, count it as being at the
// bottom
bool vscroll_bottom = vscroll->maximum() - vscroll->value() < 50;

int old_horizontal = hscroll->value();
int old_vertical = vscroll->value();

for (int i = 0; !m_log_queue.empty() && i < MAX_LOG_LINES; i++)
{
m_log_text->append(m_log_queue.front());
m_log_queue.pop();
}

if (hscroll->value() != old_horizontal)
hscroll->setValue(old_horizontal);

if (vscroll->value() != old_vertical)
{
if (vscroll_bottom)
vscroll->setValue(vscroll->maximum());
else
vscroll->setValue(old_vertical);
}
}

void LogWidget::UpdateFont()
{
QFont f;

switch (m_log_font->currentIndex())
{
case 0: // Default font
break;
case 1: // Monospace font
f = QFont(QStringLiteral("Monospace"));
f.setStyleHint(QFont::TypeWriter);
break;
}
m_log_text->setFont(f);
}

void LogWidget::CreateWidgets()
{
// Log
m_tab_log = new QWidget;
m_log_text = new QTextEdit;
m_log_wrap = new QCheckBox(tr("Word Wrap"));
m_log_font = new QComboBox;
m_log_clear = new QPushButton(tr("Clear"));

m_log_font->addItems({tr("Default Font"), tr("Monospaced Font")});

auto* log_layout = new QGridLayout;
m_tab_log->setLayout(log_layout);
log_layout->addWidget(m_log_wrap, 0, 0);
log_layout->addWidget(m_log_font, 0, 1);
log_layout->addWidget(m_log_clear, 0, 2);
log_layout->addWidget(m_log_text, 1, 0, 1, -1);

QWidget* widget = new QWidget;
widget->setLayout(log_layout);

setWidget(widget);

m_log_text->setReadOnly(true);

QPalette palette = m_log_text->palette();
palette.setColor(QPalette::Base, Qt::black);
palette.setColor(QPalette::Text, Qt::white);
m_log_text->setPalette(palette);
}

void LogWidget::ConnectWidgets()
{
connect(m_log_clear, &QPushButton::clicked, m_log_text, &QTextEdit::clear);
connect(m_log_wrap, &QCheckBox::toggled, this, &LogWidget::SaveSettings);
connect(m_log_font, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&LogWidget::SaveSettings);
connect(this, &QDockWidget::topLevelChanged, this, &LogWidget::SaveSettings);
connect(&Settings::Instance(), &Settings::LogVisibilityChanged, this,
[this](bool visible) { setHidden(!visible); });
}

void LogWidget::LoadSettings()
{
QSettings settings;

restoreGeometry(settings.value(QStringLiteral("logwidget/geometry")).toByteArray());
setFloating(settings.value(QStringLiteral("logwidget/floating")).toBool());

// Log - Wrap Lines
m_log_wrap->setChecked(settings.value(QStringLiteral("logging/wraplines")).toBool());
m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QTextEdit::WidgetWidth : QTextEdit::NoWrap);

// Log - Font Selection
// Currently "Debugger Font" is not supported as there is no Qt Debugger, defaulting to Monospace
m_log_font->setCurrentIndex(std::min(settings.value(QStringLiteral("logging/font")).toInt(), 1));
UpdateFont();
}

void LogWidget::SaveSettings()
{
QSettings settings;

settings.setValue(QStringLiteral("logwidget/geometry"), saveGeometry());
settings.setValue(QStringLiteral("logwidget/floating"), isFloating());

// Log - Wrap Lines
settings.setValue(QStringLiteral("logging/wraplines"), m_log_wrap->isChecked());
m_log_text->setLineWrapMode(m_log_wrap->isChecked() ? QTextEdit::WidgetWidth : QTextEdit::NoWrap);

// Log - Font Selection
settings.setValue(QStringLiteral("logging/font"), m_log_font->currentIndex());
UpdateFont();
}

void LogWidget::Log(LogTypes::LOG_LEVELS level, const char* text)
{
std::lock_guard<std::mutex> lock(m_log_mutex);

const char* color = "white";

switch (level)
{
case LogTypes::LOG_LEVELS::LERROR:
color = "red";
break;
case LogTypes::LOG_LEVELS::LWARNING:
color = "yellow";
break;
case LogTypes::LOG_LEVELS::LNOTICE:
color = "green";
break;
case LogTypes::LOG_LEVELS::LINFO:
color = "cyan";
break;
case LogTypes::LOG_LEVELS::LDEBUG:
color = "lightgrey";
break;
}

m_log_queue.push(QStringLiteral("%1 <font color='%2'>%3</font>")
.arg(QString::fromStdString(std::string(text).substr(0, TIMESTAMP_LENGTH)),
QString::fromStdString(color),
QString::fromStdString(std::string(text).substr(TIMESTAMP_LENGTH))));
}
@@ -12,64 +12,39 @@
#include "Common/Logging/LogManager.h"

class QCheckBox;
class QCloseEvent;
class QComboBox;
class QListWidget;
class QPushButton;
class QRadioButton;
class QVBoxLayout;
class QTabWidget;
class QTextEdit;
class QTimer;

class LoggerWidget final : public QDockWidget, LogListener
class LogWidget final : public QDockWidget, LogListener
{
Q_OBJECT
public:
explicit LoggerWidget(QWidget* parent = nullptr);
~LoggerWidget();

bool eventFilter(QObject* object, QEvent* event) override;
explicit LogWidget(QWidget* parent = nullptr);
~LogWidget();

private:
void UpdateLog();
void UpdateFont();
void CreateWidgets();
void ConnectWidgets();
void CreateMainLayout();
void LoadSettings();
void SaveSettings();

void OnTabVisibilityChanged();

void Log(LogTypes::LOG_LEVELS level, const char* text) override;

// Log
QCheckBox* m_log_wrap;
QComboBox* m_log_font;
QPushButton* m_log_clear;
QVBoxLayout* m_main_layout;
QTabWidget* m_tab_widget;
QTextEdit* m_log_text;
QWidget* m_tab_log;

// Configuration
QWidget* m_tab_config;
QRadioButton* m_verbosity_notice;
QRadioButton* m_verbosity_error;
QRadioButton* m_verbosity_warning;
QRadioButton* m_verbosity_info;
QCheckBox* m_out_file;
QCheckBox* m_out_console;
QCheckBox* m_out_window;
QPushButton* m_types_toggle;
QListWidget* m_types_list;

QTimer* m_timer;

std::mutex m_log_mutex;
std::queue<QString> m_log_queue;

bool m_all_enabled = true;
bool m_block_save = false;
};

This file was deleted.

@@ -66,7 +66,8 @@
<QtMoc Include="Config\Mapping\MappingButton.h" />
<QtMoc Include="Config\Mapping\MappingWidget.h" />
<QtMoc Include="Config\Mapping\MappingWindow.h" />
<QtMoc Include="Config\LoggerWidget.h" />
<QtMoc Include="Config\LogConfigWidget.h" />
<QtMoc Include="Config\LogWidget.h" />
<QtMoc Include="Config\Graphics\AdvancedWidget.h" />
<QtMoc Include="Config\Graphics\EnhancementsWidget.h" />
<QtMoc Include="Config\Graphics\GeneralWidget.h" />
@@ -134,7 +135,8 @@
<ClCompile Include="$(QtMocOutPrefix)IOWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)GridProxyModel.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ListProxyModel.cpp" />
<ClCompile Include="$(QtMocOutPrefix)LoggerWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)LogConfigWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)LogWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MainWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MappingButton.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MappingWidget.cpp" />
@@ -184,7 +186,8 @@
<ClCompile Include="Config\Mapping\WiimoteEmuExtension.cpp" />
<ClCompile Include="Config\Mapping\WiimoteEmuGeneral.cpp" />
<ClCompile Include="Config\Mapping\WiimoteEmuMotionControl.cpp" />
<ClCompile Include="Config\LoggerWidget.cpp" />
<ClCompile Include="Config\LogConfigWidget.cpp" />
<ClCompile Include="Config\LogWidget.cpp" />
<ClCompile Include="Config\PropertiesDialog.cpp" />
<ClCompile Include="Config\SettingsWindow.cpp" />
<ClCompile Include="GameList\GameFile.cpp" />
@@ -223,6 +226,8 @@
</ItemGroup>
<!--Put standard C/C++ headers here-->
<ItemGroup>
<ClInclude Include="Config\LogWidget.h" />
<ClInclude Include="Config\LogConfigWidget.h" />
<ClInclude Include="Config\Mapping\GCKeyboardEmu.h" />
<ClInclude Include="Config\Mapping\GCPadEmu.h" />
<ClInclude Include="Config\Mapping\GCPadWiiU.h" />
@@ -41,7 +41,8 @@
#include "DolphinQt2/AboutDialog.h"
#include "DolphinQt2/Config/ControllersWindow.h"
#include "DolphinQt2/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt2/Config/LoggerWidget.h"
#include "DolphinQt2/Config/LogConfigWidget.h"
#include "DolphinQt2/Config/LogWidget.h"
#include "DolphinQt2/Config/Mapping/MappingWindow.h"
#include "DolphinQt2/Config/SettingsWindow.h"
#include "DolphinQt2/Host.h"
@@ -147,7 +148,8 @@ void MainWindow::CreateComponents()
m_controllers_window = new ControllersWindow(this);
m_settings_window = new SettingsWindow(this);
m_hotkey_window = new MappingWindow(this, 0);
m_logger_widget = new LoggerWidget(this);
m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this);

connect(this, &MainWindow::EmulationStarted, m_settings_window,
&SettingsWindow::EmulationStarted);
@@ -310,7 +312,12 @@ void MainWindow::ConnectStack()
m_stack->addWidget(m_game_list);

setCentralWidget(m_stack);
addDockWidget(Qt::RightDockWidgetArea, m_logger_widget);

setTabPosition(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea, QTabWidget::North);
addDockWidget(Qt::RightDockWidgetArea, m_log_widget);
addDockWidget(Qt::RightDockWidgetArea, m_log_config_widget);

tabifyDockWidget(m_log_widget, m_log_config_widget);
}

void MainWindow::Open()
@@ -18,7 +18,8 @@

struct BootParameters;
class HotkeyScheduler;
class LoggerWidget;
class LogConfigWidget;
class LogWidget;
class MappingWindow;
class NetPlayClient;
class NetPlayDialog;
@@ -136,5 +137,6 @@ class MainWindow final : public QMainWindow
NetPlayDialog* m_netplay_dialog;
NetPlaySetupDialog* m_netplay_setup_dialog;
GraphicsWindow* m_graphics_window;
LoggerWidget* m_logger_widget;
LogWidget* m_log_widget;
LogConfigWidget* m_log_config_widget;
};