-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Qt/GameConfigWidget: Complete overhaul
- Loading branch information
Showing
8 changed files
with
491 additions
and
364 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
// Copyright 2018 Dolphin Emulator Project | ||
// Licensed under GPLv2+ | ||
// Refer to the license.txt file included. | ||
|
||
#include "DolphinQt/Config/GameConfigEdit.h" | ||
|
||
#include <QAbstractItemView> | ||
#include <QCompleter> | ||
#include <QDesktopServices> | ||
#include <QFile> | ||
#include <QMenu> | ||
#include <QMessageBox> | ||
#include <QScrollBar> | ||
#include <QStringListModel> | ||
#include <QTextCursor> | ||
#include <QTextEdit> | ||
#include <QVBoxLayout> | ||
#include <QWhatsThis> | ||
|
||
#include "DolphinQt/Config/GameConfigHighlighter.h" | ||
|
||
GameConfigEdit::GameConfigEdit(QWidget* parent, const QString& path) : m_path(path) | ||
{ | ||
LoadFile(); | ||
|
||
setAcceptRichText(false); | ||
setContextMenuPolicy(Qt::CustomContextMenu); | ||
|
||
new GameConfigHighlighter(document()); | ||
|
||
AddDescription(QStringLiteral("Core"), | ||
tr("Section that contains most CPU and Hardware related settings.")); | ||
|
||
AddDescription(QStringLiteral("CPUThread"), tr("Controls whether or not Dual Core should be " | ||
"enabled. Can improve performance but can also " | ||
"cause issues. Defaults to <b>True</b>")); | ||
|
||
AddDescription(QStringLiteral("FastDiscSpeed"), | ||
tr("Shortens loading times but may break some games. Can have negative effects on " | ||
"performance. Defaults to <b>False</b>")); | ||
|
||
AddDescription(QStringLiteral("MMU"), tr("Controls whether or not the Memory Management Unit " | ||
"should be emulated fully. Few games require it.")); | ||
|
||
AddDescription( | ||
QStringLiteral("DSPHLE"), | ||
tr("Controls whether to use high or low-level DSP emulation. Defaults to <b>True</b>")); | ||
|
||
AddDescription( | ||
QStringLiteral("JITFollowBranch"), | ||
tr("Tries to translate branches ahead of time, improving performance in most cases. Defaults " | ||
"to <b>True</b>")); | ||
|
||
AddDescription(QStringLiteral("Gecko"), tr("Section that contains all Gecko cheat codes.")); | ||
|
||
AddDescription(QStringLiteral("ActionReplay"), | ||
tr("Section that contains all Action Replay cheat codes.")); | ||
|
||
AddDescription(QStringLiteral("Video_Settings"), | ||
tr("Section that contains all graphics related settings.")); | ||
|
||
m_completer = new QCompleter(this); | ||
|
||
auto* completion_model = new QStringListModel; | ||
completion_model->setStringList(m_completions); | ||
|
||
m_completer->setModel(completion_model); | ||
m_completer->setModelSorting(QCompleter::UnsortedModel); | ||
m_completer->setCompletionMode(QCompleter::PopupCompletion); | ||
m_completer->setWidget(this); | ||
|
||
Connect(); | ||
} | ||
|
||
void GameConfigEdit::AddDescription(const QString& keyword, const QString& description) | ||
{ | ||
m_keyword_map[keyword] = description; | ||
m_completions << keyword; | ||
} | ||
|
||
void GameConfigEdit::LoadFile() | ||
{ | ||
QFile file(m_path); | ||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) | ||
return; | ||
|
||
setPlainText(QString::fromStdString(file.readAll().toStdString())); | ||
} | ||
|
||
void GameConfigEdit::SaveFile() | ||
{ | ||
if (!isVisible() || isReadOnly()) | ||
return; | ||
|
||
QFile file(m_path); | ||
|
||
const QByteArray contents = toPlainText().toUtf8(); | ||
|
||
if (contents.isEmpty()) | ||
{ | ||
if (file.exists()) | ||
file.remove(); | ||
return; | ||
} | ||
|
||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) | ||
return; | ||
|
||
if (!file.write(contents)) | ||
QMessageBox::warning(this, tr("Warning"), tr("Failed to write config file!")); | ||
} | ||
|
||
void GameConfigEdit::Connect() | ||
{ | ||
connect(this, &QTextEdit::textChanged, this, &GameConfigEdit::SaveFile); | ||
connect(this, &QTextEdit::selectionChanged, this, &GameConfigEdit::OnSelectionChanged); | ||
connect(this, &QTextEdit::customContextMenuRequested, this, &GameConfigEdit::OnContextMenu); | ||
connect(m_completer, static_cast<void (QCompleter::*)(const QString&)>(&QCompleter::activated), | ||
this, &GameConfigEdit::OnAutoComplete); | ||
} | ||
|
||
void GameConfigEdit::OnSelectionChanged() | ||
{ | ||
const QString& keyword = textCursor().selectedText(); | ||
|
||
if (m_keyword_map.count(keyword)) | ||
QWhatsThis::showText(QCursor::pos(), m_keyword_map[keyword], this); | ||
} | ||
|
||
void GameConfigEdit::AddBoolOption(QMenu* menu, const QString& name, const QString& section, | ||
const QString& key) | ||
{ | ||
auto* option = menu->addMenu(name); | ||
|
||
option->addAction(tr("On"), this, | ||
[this, section, key] { SetOption(section, key, QStringLiteral("True")); }); | ||
option->addAction(tr("Off"), this, | ||
[this, section, key] { SetOption(section, key, QStringLiteral("False")); }); | ||
} | ||
|
||
void GameConfigEdit::SetOption(const QString& section, const QString& key, const QString& value) | ||
{ | ||
auto section_cursor = document()->find(QRegExp(QStringLiteral("^\\[%1\\]").arg(section)), 0); | ||
|
||
// Check if the section this belongs in can be found | ||
if (section_cursor.isNull()) | ||
{ | ||
append(QStringLiteral("[%1]\n\n%2 = %3\n").arg(section).arg(key).arg(value)); | ||
} | ||
else | ||
{ | ||
auto value_cursor = | ||
document()->find(QRegExp(QStringLiteral("^%1 = .*").arg(key)), section_cursor); | ||
|
||
const QString new_line = QStringLiteral("%1 = %2").arg(key).arg(value); | ||
|
||
// Check if the value that has to be set already exists | ||
if (value_cursor.isNull()) | ||
{ | ||
section_cursor.clearSelection(); | ||
section_cursor.insertText(QStringLiteral("\n") + new_line); | ||
} | ||
else | ||
{ | ||
value_cursor.insertText(new_line); | ||
} | ||
} | ||
} | ||
|
||
QString GameConfigEdit::GetTextUnderCursor() | ||
{ | ||
QTextCursor tc = textCursor(); | ||
tc.select(QTextCursor::WordUnderCursor); | ||
return tc.selectedText(); | ||
} | ||
|
||
void GameConfigEdit::OnContextMenu(const QPoint& pos) | ||
{ | ||
auto* menu = createStandardContextMenu(pos); | ||
|
||
menu->addSeparator(); | ||
|
||
menu->addAction(tr("Refresh"), this, &GameConfigEdit::LoadFile); | ||
menu->addAction(tr("Open in External Editor"), this, &GameConfigEdit::OpenExternalEditor); | ||
|
||
if (!isReadOnly()) | ||
{ | ||
menu->addSeparator(); | ||
auto* core_menu = menu->addMenu(tr("Core")); | ||
|
||
AddBoolOption(core_menu, tr("Dual Core"), QStringLiteral("Core"), QStringLiteral("CPUThread")); | ||
AddBoolOption(core_menu, tr("MMU"), QStringLiteral("Core"), QStringLiteral("MMU")); | ||
|
||
auto* video_menu = menu->addMenu(tr("Video")); | ||
|
||
AddBoolOption(video_menu, tr("Store EFB Copies to Texture Only"), | ||
QStringLiteral("Video_Settings"), QStringLiteral("EFBToTextureEnable")); | ||
|
||
AddBoolOption(video_menu, tr("Store XFB Copies to Texture Only"), | ||
QStringLiteral("Video_Settings"), QStringLiteral("XFBToTextureEnable")); | ||
|
||
{ | ||
auto* texture_cache = video_menu->addMenu(tr("Texture Cache")); | ||
texture_cache->addAction(tr("Safe"), this, [this] { | ||
SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), | ||
QStringLiteral("0")); | ||
}); | ||
texture_cache->addAction(tr("Medium"), this, [this] { | ||
SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), | ||
QStringLiteral("512")); | ||
}); | ||
texture_cache->addAction(tr("Fast"), this, [this] { | ||
SetOption(QStringLiteral("Video_Settings"), QStringLiteral("SafeTextureCacheColorSamples"), | ||
QStringLiteral("128")); | ||
}); | ||
} | ||
} | ||
|
||
menu->exec(QCursor::pos()); | ||
} | ||
|
||
void GameConfigEdit::OnAutoComplete(const QString& completion) | ||
{ | ||
QTextCursor cursor = textCursor(); | ||
int extra = completion.length() - m_completer->completionPrefix().length(); | ||
cursor.movePosition(QTextCursor::Left); | ||
cursor.movePosition(QTextCursor::EndOfWord); | ||
cursor.insertText(completion.right(extra)); | ||
setTextCursor(cursor); | ||
} | ||
|
||
void GameConfigEdit::OpenExternalEditor() | ||
{ | ||
QFile file(m_path); | ||
|
||
if (!file.exists()) | ||
{ | ||
if (isReadOnly()) | ||
return; | ||
|
||
file.open(QIODevice::WriteOnly); | ||
file.close(); | ||
} | ||
|
||
QDesktopServices::openUrl(QUrl::fromLocalFile(m_path)); | ||
} | ||
|
||
void GameConfigEdit::keyPressEvent(QKeyEvent* e) | ||
{ | ||
if (m_completer->popup()->isVisible()) | ||
{ | ||
// The following keys are forwarded by the completer to the widget | ||
switch (e->key()) | ||
{ | ||
case Qt::Key_Enter: | ||
case Qt::Key_Return: | ||
case Qt::Key_Escape: | ||
case Qt::Key_Tab: | ||
case Qt::Key_Backtab: | ||
e->ignore(); | ||
return; // let the completer do default behavior | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
QTextEdit::keyPressEvent(e); | ||
|
||
const static QString end_of_word = QStringLiteral("~!@#$%^&*()_+{}|:\"<>?,./;'\\-="); | ||
|
||
QString completion_prefix = GetTextUnderCursor(); | ||
|
||
if (e->text().isEmpty() || completion_prefix.length() < 2 || | ||
end_of_word.contains(e->text().right(1))) | ||
{ | ||
m_completer->popup()->hide(); | ||
return; | ||
} | ||
|
||
if (completion_prefix != m_completer->completionPrefix()) | ||
{ | ||
m_completer->setCompletionPrefix(completion_prefix); | ||
m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0)); | ||
} | ||
QRect cr = cursorRect(); | ||
cr.setWidth(m_completer->popup()->sizeHintForColumn(0) + | ||
m_completer->popup()->verticalScrollBar()->sizeHint().width()); | ||
m_completer->complete(cr); // popup it up! | ||
} | ||
|
||
void GameConfigEdit::focusInEvent(QFocusEvent* e) | ||
{ | ||
m_completer->setWidget(this); | ||
QTextEdit::focusInEvent(e); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright 2018 Dolphin Emulator Project | ||
// Licensed under GPLv2+ | ||
// Refer to the license.txt file included. | ||
|
||
#pragma once | ||
|
||
#include <QMap> | ||
#include <QString> | ||
#include <QStringList> | ||
#include <QTextEdit> | ||
|
||
class QCompleter; | ||
|
||
class GameConfigEdit : public QTextEdit | ||
{ | ||
public: | ||
explicit GameConfigEdit(QWidget* parent, const QString& path); | ||
|
||
protected: | ||
void keyPressEvent(QKeyEvent* e) override; | ||
void focusInEvent(QFocusEvent* e) override; | ||
|
||
private: | ||
void Connect(); | ||
|
||
void LoadFile(); | ||
void SaveFile(); | ||
|
||
void OnSelectionChanged(); | ||
void OnContextMenu(const QPoint& pos); | ||
void OnAutoComplete(const QString& completion); | ||
void OpenExternalEditor(); | ||
|
||
void SetReadOnly(bool read_only); | ||
|
||
QString GetTextUnderCursor(); | ||
|
||
void AddBoolOption(QMenu* menu, const QString& name, const QString& section, const QString& key); | ||
|
||
void SetOption(const QString& section, const QString& key, const QString& value); | ||
|
||
void AddDescription(const QString& keyword, const QString& description); | ||
|
||
QCompleter* m_completer; | ||
QStringList m_completions; | ||
|
||
const QString m_path; | ||
|
||
QMap<QString, QString> m_keyword_map; | ||
}; |
Oops, something went wrong.