mirror of
https://github.com/libretro/RetroArch
synced 2025-01-29 18:32:44 +00:00
03c8c12740
This reverts commit 6de5ebadf9462ea48978850c811fea592e592aaf.
2982 lines
89 KiB
C++
2982 lines
89 KiB
C++
#include <QCloseEvent>
|
|
#include <QSettings>
|
|
#include <QResizeEvent>
|
|
#include <QMessageBox>
|
|
#include <QPainter>
|
|
#include <QScrollArea>
|
|
#include <QVBoxLayout>
|
|
#include <QGroupBox>
|
|
#include <QFormLayout>
|
|
#include <QSpinBox>
|
|
#include <QDoubleSpinBox>
|
|
#include <QCheckBox>
|
|
#include <QComboBox>
|
|
#include <QPushButton>
|
|
#include <QToolButton>
|
|
#include <QMenu>
|
|
#include <QFileInfo>
|
|
#include <QFileDialog>
|
|
#include <QTimer>
|
|
#include <QMainWindow>
|
|
#include <QApplication>
|
|
#include <QLineEdit>
|
|
#include <QDialogButtonBox>
|
|
#include <QHBoxLayout>
|
|
#include <QColor>
|
|
#include <QColorDialog>
|
|
#include <QLabel>
|
|
#include <QBitmap>
|
|
#include <QStackedLayout>
|
|
#include <QScrollBar>
|
|
|
|
#include "qt_dialogs.h"
|
|
#include "../ui_qt.h"
|
|
|
|
#ifndef CXX_BUILD
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../../config.h"
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <string/stdstring.h>
|
|
#include <streams/file_stream.h>
|
|
#include <file/file_path.h>
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../../../menu/menu_driver.h"
|
|
#include "../../../menu/menu_entries.h"
|
|
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
|
#include "../../../menu/menu_shader.h"
|
|
#endif
|
|
#endif
|
|
|
|
#include "../../../command.h"
|
|
#include "../../../core_info.h"
|
|
#include "../../../core_option_manager.h"
|
|
#include "../../../configuration.h"
|
|
#include "../../../file_path_special.h"
|
|
#include "../../../msg_hash.h"
|
|
#include "../../../paths.h"
|
|
#include "../../../retroarch.h"
|
|
|
|
#ifndef CXX_BUILD
|
|
}
|
|
#endif
|
|
|
|
#include "qt_options.h"
|
|
|
|
#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
|
|
/* https://support.microsoft.com/en-us/kb/980263 */
|
|
#pragma execution_character_set("utf-8")
|
|
#pragma warning(disable:4566)
|
|
#endif
|
|
|
|
static inline bool comp_string_lower(const QString &lhs, const QString &rhs)
|
|
{
|
|
return lhs.toLower() < rhs.toLower();
|
|
}
|
|
|
|
static inline bool comp_hash_ui_display_name_key_lower(const QHash<QString, QString> &lhs, const QHash<QString, QString> &rhs)
|
|
{
|
|
return lhs.value("ui_display_name").toLower() < rhs.value("ui_display_name").toLower();
|
|
}
|
|
|
|
PlaylistEntryDialog::PlaylistEntryDialog(MainWindow *mainwindow, QWidget *parent) :
|
|
QDialog(parent)
|
|
,m_mainwindow(mainwindow)
|
|
,m_settings(mainwindow->settings())
|
|
,m_nameLineEdit(new QLineEdit(this))
|
|
,m_pathLineEdit(new QLineEdit(this))
|
|
,m_extensionsLineEdit(new QLineEdit(this))
|
|
,m_coreComboBox(new QComboBox(this))
|
|
,m_databaseComboBox(new QComboBox(this))
|
|
,m_extensionArchiveCheckBox(new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_FILTER_INSIDE_ARCHIVES), this))
|
|
{
|
|
QFormLayout *form = new QFormLayout();
|
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
QVBoxLayout *databaseVBoxLayout = new QVBoxLayout();
|
|
QHBoxLayout *pathHBoxLayout = new QHBoxLayout();
|
|
QHBoxLayout *extensionHBoxLayout = new QHBoxLayout();
|
|
QLabel *databaseLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS), this);
|
|
QToolButton *pathPushButton = new QToolButton(this);
|
|
|
|
pathPushButton->setText(QStringLiteral("..."));
|
|
|
|
pathHBoxLayout->addWidget(m_pathLineEdit);
|
|
pathHBoxLayout->addWidget(pathPushButton);
|
|
|
|
databaseVBoxLayout->addWidget(m_databaseComboBox);
|
|
databaseVBoxLayout->addWidget(databaseLabel);
|
|
|
|
extensionHBoxLayout->addWidget(m_extensionsLineEdit);
|
|
extensionHBoxLayout->addWidget(m_extensionArchiveCheckBox);
|
|
|
|
m_extensionsLineEdit->setPlaceholderText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_EXTENSIONS_PLACEHOLDER));
|
|
|
|
/* Ensure placeholder text is completely visible. */
|
|
m_extensionsLineEdit->setMinimumWidth(QFontMetrics(m_extensionsLineEdit->font()).boundingRect(m_extensionsLineEdit->placeholderText()).width() + m_extensionsLineEdit->frameSize().width());
|
|
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY));
|
|
|
|
form->setFormAlignment(Qt::AlignCenter);
|
|
form->setLabelAlignment(Qt::AlignCenter);
|
|
|
|
setLayout(new QVBoxLayout(this));
|
|
|
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
|
|
|
connect(this, SIGNAL(accepted()), this, SLOT(onAccepted()));
|
|
connect(this, SIGNAL(rejected()), this, SLOT(onRejected()));
|
|
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_NAME), m_nameLineEdit);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_PATH), pathHBoxLayout);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE), m_coreComboBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE), databaseVBoxLayout);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_EXTENSIONS), extensionHBoxLayout);
|
|
|
|
qobject_cast<QVBoxLayout*>(layout())->addLayout(form);
|
|
layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
layout()->addWidget(buttonBox);
|
|
|
|
connect(pathPushButton, SIGNAL(clicked()), this, SLOT(onPathClicked()));
|
|
}
|
|
|
|
bool PlaylistEntryDialog::filterInArchive()
|
|
{
|
|
return m_extensionArchiveCheckBox->isChecked();
|
|
}
|
|
|
|
void PlaylistEntryDialog::onPathClicked()
|
|
{
|
|
QString filePath = QFileDialog::getOpenFileName(this);
|
|
|
|
if (filePath.isEmpty())
|
|
return;
|
|
|
|
m_pathLineEdit->setText(filePath);
|
|
}
|
|
|
|
void PlaylistEntryDialog::loadPlaylistOptions()
|
|
{
|
|
unsigned i, j;
|
|
core_info_list_t *core_info_list = NULL;
|
|
|
|
m_nameLineEdit->clear();
|
|
m_pathLineEdit->clear();
|
|
m_coreComboBox->clear();
|
|
m_databaseComboBox->clear();
|
|
|
|
m_coreComboBox->addItem(
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK));
|
|
m_databaseComboBox->addItem(
|
|
QString("<")
|
|
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
|
|
+ ">",
|
|
QFileInfo(m_mainwindow->getCurrentPlaylistPath()).fileName().remove(".lpl"));
|
|
|
|
core_info_get_list(&core_info_list);
|
|
|
|
if (core_info_list && core_info_list->count > 0)
|
|
{
|
|
QVector<QHash<QString, QString> > allCores;
|
|
QStringList allDatabases;
|
|
|
|
for (i = 0; i < core_info_list->count; i++)
|
|
{
|
|
QString ui_display_name;
|
|
QHash<QString, QString> hash;
|
|
const core_info_t *core = &core_info_list->list[i];
|
|
QStringList databases = string_split_to_qt(QString(core->databases), '|');
|
|
|
|
hash["core_name"] = core->core_name;
|
|
hash["core_display_name"] = core->display_name;
|
|
hash["core_path"] = core->path;
|
|
hash["core_databases"] = core->databases;
|
|
|
|
ui_display_name = hash.value("core_name");
|
|
|
|
if (ui_display_name.isEmpty())
|
|
ui_display_name = hash.value("core_display_name");
|
|
if (ui_display_name.isEmpty())
|
|
ui_display_name = QFileInfo(
|
|
hash.value("core_path")).fileName();
|
|
|
|
if (ui_display_name.isEmpty())
|
|
continue;
|
|
|
|
hash["ui_display_name"] = ui_display_name;
|
|
|
|
for (j = 0; static_cast<int>(j) < databases.count(); j++)
|
|
{
|
|
QString database = databases.at(static_cast<int>(j));
|
|
|
|
if (database.isEmpty())
|
|
continue;
|
|
|
|
if (!allDatabases.contains(database))
|
|
allDatabases.append(database);
|
|
}
|
|
|
|
if (!allCores.contains(hash))
|
|
allCores.append(hash);
|
|
}
|
|
|
|
std::sort(allCores.begin(), allCores.end(), comp_hash_ui_display_name_key_lower);
|
|
std::sort(allDatabases.begin(), allDatabases.end(), comp_string_lower);
|
|
|
|
for (j = 0; static_cast<int>(j) < allCores.count(); j++)
|
|
{
|
|
const QHash<QString, QString> &hash = allCores.at(static_cast<int>(j));
|
|
|
|
m_coreComboBox->addItem(hash.value("ui_display_name"), QVariant::fromValue(hash));
|
|
}
|
|
|
|
for (j = 0; static_cast<int>(j) < allDatabases.count(); j++)
|
|
{
|
|
QString database = allDatabases.at(static_cast<int>(j));
|
|
m_databaseComboBox->addItem(database, database);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PlaylistEntryDialog::nameFieldEnabled()
|
|
{
|
|
return m_nameLineEdit->isEnabled();
|
|
}
|
|
|
|
void PlaylistEntryDialog::setEntryValues(
|
|
const QHash<QString, QString> &contentHash)
|
|
{
|
|
QString db;
|
|
QString coreName = contentHash.value("core_name");
|
|
int foundDB = 0;
|
|
int i = 0;
|
|
|
|
loadPlaylistOptions();
|
|
|
|
if (contentHash.isEmpty())
|
|
{
|
|
m_nameLineEdit->setText(
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE));
|
|
m_pathLineEdit->setText(
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE));
|
|
m_nameLineEdit->setEnabled(false);
|
|
m_pathLineEdit->setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
m_nameLineEdit->setText(contentHash.value("label"));
|
|
m_pathLineEdit->setText(contentHash.value("path"));
|
|
m_nameLineEdit->setEnabled(true);
|
|
m_pathLineEdit->setEnabled(true);
|
|
}
|
|
|
|
for (i = 0; i < m_coreComboBox->count(); i++)
|
|
{
|
|
const QHash<QString, QString> hash = m_coreComboBox->itemData(i, Qt::UserRole).value<QHash<QString, QString> >();
|
|
|
|
if (hash.isEmpty() || coreName.isEmpty())
|
|
continue;
|
|
|
|
if (hash.value("core_name") == coreName)
|
|
{
|
|
m_coreComboBox->setCurrentIndex(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
db = contentHash.value("db_name");
|
|
|
|
if (!db.isEmpty())
|
|
{
|
|
foundDB = m_databaseComboBox->findText(db);
|
|
|
|
if (foundDB >= 0)
|
|
m_databaseComboBox->setCurrentIndex(foundDB);
|
|
}
|
|
}
|
|
|
|
const QHash<QString, QString> PlaylistEntryDialog::getSelectedCore()
|
|
{
|
|
return m_coreComboBox->currentData(Qt::UserRole).value<QHash<QString, QString> >();
|
|
}
|
|
|
|
const QString PlaylistEntryDialog::getSelectedName()
|
|
{
|
|
return m_nameLineEdit->text();
|
|
}
|
|
|
|
const QString PlaylistEntryDialog::getSelectedPath()
|
|
{
|
|
return m_pathLineEdit->text();
|
|
}
|
|
|
|
const QString PlaylistEntryDialog::getSelectedDatabase()
|
|
{
|
|
return m_databaseComboBox->currentData(Qt::UserRole).toString();
|
|
}
|
|
|
|
const QStringList PlaylistEntryDialog::getSelectedExtensions()
|
|
{
|
|
QStringList list;
|
|
QString text = m_extensionsLineEdit->text();
|
|
|
|
/* Otherwise it would create a QStringList with a single blank entry... */
|
|
if (!text.isEmpty())
|
|
list = string_split_to_qt(text, ' ');
|
|
return list;
|
|
}
|
|
|
|
void PlaylistEntryDialog::onAccepted()
|
|
{
|
|
}
|
|
|
|
void PlaylistEntryDialog::onRejected()
|
|
{
|
|
}
|
|
|
|
bool PlaylistEntryDialog::showDialog(const QHash<QString, QString> &hash)
|
|
{
|
|
loadPlaylistOptions();
|
|
setEntryValues(hash);
|
|
|
|
if (exec() == QDialog::Accepted)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void PlaylistEntryDialog::hideDialog()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
CoreInfoDialog::CoreInfoDialog(MainWindow *mainwindow, QWidget *parent) :
|
|
QDialog(parent)
|
|
,m_formLayout(new QFormLayout())
|
|
,m_mainwindow(mainwindow)
|
|
{
|
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
|
|
|
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
|
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION));
|
|
|
|
m_formLayout->setFormAlignment(Qt::AlignCenter);
|
|
m_formLayout->setLabelAlignment(Qt::AlignCenter);
|
|
|
|
setLayout(new QVBoxLayout());
|
|
|
|
qobject_cast<QVBoxLayout*>(layout())->addLayout(m_formLayout);
|
|
layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
layout()->addWidget(buttonBox);
|
|
}
|
|
|
|
void CoreInfoDialog::showCoreInfo()
|
|
{
|
|
int row = 0;
|
|
int row_count = m_formLayout->rowCount();
|
|
int i = 0;
|
|
QVector<QHash<QString, QString> > info_list
|
|
= m_mainwindow->getCoreInfo();
|
|
|
|
if (row_count > 0)
|
|
{
|
|
for (row = 0; row < row_count; row++)
|
|
{
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
|
|
/* removeRow() and takeRow() was only added in 5.8! */
|
|
m_formLayout->removeRow(0);
|
|
#else
|
|
/* something is buggy here...
|
|
* sometimes items appear duplicated, and other times not */
|
|
QLayoutItem *item = m_formLayout->itemAt(0);
|
|
QWidget *w = NULL;
|
|
|
|
if (item)
|
|
{
|
|
w = item->widget();
|
|
|
|
if (w)
|
|
{
|
|
QWidget *label = m_formLayout->labelForField(w);
|
|
|
|
if (label)
|
|
delete label;
|
|
|
|
m_formLayout->removeWidget(w);
|
|
|
|
delete w;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (info_list.count() == 0)
|
|
return;
|
|
|
|
for (i = 0; i < info_list.count(); i++)
|
|
{
|
|
const QHash<QString, QString> &line = info_list.at(i);
|
|
QLabel *label = new QLabel(line.value("key"));
|
|
CoreInfoLabel *value = new CoreInfoLabel(line.value("value"));
|
|
QString labelStyle = line.value("label_style");
|
|
QString valueStyle = line.value("value_style");
|
|
|
|
if (!labelStyle.isEmpty())
|
|
label->setStyleSheet(labelStyle);
|
|
|
|
if (!valueStyle.isEmpty())
|
|
value->setStyleSheet(valueStyle);
|
|
|
|
m_formLayout->addRow(label, value);
|
|
}
|
|
|
|
show();
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
QPixmap getColorizedPixmap(const QPixmap& oldPixmap, const QColor& color)
|
|
{
|
|
QPixmap pixmap = oldPixmap;
|
|
QBitmap mask = pixmap.createMaskFromColor(Qt::transparent, Qt::MaskInColor);
|
|
pixmap.fill(color);
|
|
pixmap.setMask(mask);
|
|
return pixmap;
|
|
}
|
|
|
|
QColor getLabelColor(const QString& objectName)
|
|
{
|
|
QLabel dummyColor;
|
|
dummyColor.setObjectName(objectName);
|
|
dummyColor.ensurePolished();
|
|
return dummyColor.palette().color(QPalette::WindowText);
|
|
}
|
|
|
|
/* stolen from Qt Creator */
|
|
class SmartScrollArea : public QScrollArea
|
|
{
|
|
public:
|
|
explicit SmartScrollArea(QWidget *parent) :
|
|
QScrollArea(parent)
|
|
{
|
|
setFrameStyle(QFrame::NoFrame | QFrame::Plain);
|
|
viewport()->setAutoFillBackground(false);
|
|
setWidgetResizable(true);
|
|
}
|
|
private:
|
|
void resizeEvent(QResizeEvent *event) final
|
|
{
|
|
QWidget *inner = widget();
|
|
|
|
if (inner)
|
|
{
|
|
int fw = frameWidth() * 2;
|
|
QSize innerSize = event->size() - QSize(fw, fw);
|
|
QSize innerSizeHint = inner->minimumSizeHint();
|
|
|
|
/* Widget wants to be bigger than available space */
|
|
if (innerSizeHint.height() > innerSize.height())
|
|
{
|
|
innerSize.setWidth(innerSize.width() - scrollBarWidth());
|
|
innerSize.setHeight(innerSizeHint.height());
|
|
}
|
|
inner->resize(innerSize);
|
|
}
|
|
QScrollArea::resizeEvent(event);
|
|
}
|
|
|
|
QSize minimumSizeHint() const final
|
|
{
|
|
static const int max_min_width = 250;
|
|
static const int max_min_height = 250;
|
|
QWidget *inner = widget();
|
|
|
|
if (inner)
|
|
{
|
|
int fw = frameWidth() * 2;
|
|
QSize minSize = inner->minimumSizeHint();
|
|
|
|
minSize += QSize(fw, fw);
|
|
minSize += QSize(scrollBarWidth(), 0);
|
|
minSize.setWidth(qMin(minSize.width(), max_min_width));
|
|
minSize.setHeight(qMin(minSize.height(), max_min_height));
|
|
return minSize;
|
|
}
|
|
return QSize(0, 0);
|
|
}
|
|
|
|
bool event(QEvent *event) final
|
|
{
|
|
if (event->type() == QEvent::LayoutRequest)
|
|
updateGeometry();
|
|
return QScrollArea::event(event);
|
|
}
|
|
|
|
int scrollBarWidth() const
|
|
{
|
|
SmartScrollArea *that = const_cast<SmartScrollArea *>(this);
|
|
QWidgetList list = that->scrollBarWidgets(Qt::AlignRight);
|
|
if (list.isEmpty())
|
|
return 0;
|
|
return list.first()->sizeHint().width();
|
|
}
|
|
};
|
|
|
|
ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
|
|
QDialog(mainwindow)
|
|
,m_optionsList(new QListWidget(this))
|
|
,m_optionsStack(new QStackedLayout)
|
|
{
|
|
int width;
|
|
QGridLayout *layout = new QGridLayout(this);
|
|
QLabel *m_headerLabel = new QLabel(this);
|
|
/* Header label with large font and a bit of spacing
|
|
* (align with group boxes) */
|
|
QFont headerLabelFont = m_headerLabel->font();
|
|
const int pointSize = headerLabelFont.pointSize();
|
|
QHBoxLayout *headerHLayout = new QHBoxLayout;
|
|
const int leftMargin = QApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
|
|
|
|
#if (QT_VERSION > QT_VERSION_CHECK(6, 0, 0))
|
|
m_optionsStack->setContentsMargins(0, 0, 0, 0);
|
|
#else
|
|
m_optionsStack->setMargin(0);
|
|
#endif
|
|
|
|
headerLabelFont.setBold(true);
|
|
|
|
/* Paranoia: Should a font be set in pixels... */
|
|
if (pointSize > 0)
|
|
headerLabelFont.setPointSize(pointSize + 2);
|
|
|
|
m_headerLabel->setFont(headerLabelFont);
|
|
|
|
headerHLayout->addSpacerItem(new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
|
|
headerHLayout->addWidget(m_headerLabel);
|
|
|
|
addCategory(new DriversCategory(this));
|
|
addCategory(new VideoCategory(this));
|
|
addCategory(new AudioCategory(this));
|
|
addCategory(new InputCategory(this));
|
|
addCategory(new LatencyCategory(this));
|
|
addCategory(new CoreCategory(this));
|
|
addCategory(new ConfigurationCategory(this));
|
|
addCategory(new SavingCategory(this));
|
|
addCategory(new LoggingCategory(this));
|
|
addCategory(new FrameThrottleCategory(this));
|
|
addCategory(new RecordingCategory(this));
|
|
addCategory(new OnscreenDisplayCategory(this));
|
|
addCategory(new UserInterfaceCategory(mainwindow, this));
|
|
addCategory(new AIServiceCategory(this));
|
|
addCategory(new AchievementsCategory(this));
|
|
addCategory(new NetworkCategory(this));
|
|
addCategory(new PlaylistsCategory(this));
|
|
addCategory(new UserCategory(this));
|
|
addCategory(new DirectoryCategory(this));
|
|
|
|
width =
|
|
m_optionsList->sizeHintForColumn(0)
|
|
+ m_optionsList->frameWidth() * 2
|
|
+ 5;
|
|
width += m_optionsList->verticalScrollBar()->sizeHint().width();
|
|
|
|
m_optionsList->setMaximumWidth(width);
|
|
m_optionsList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
|
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE));
|
|
|
|
layout->addWidget(m_optionsList, 0, 0, 2, 1);
|
|
layout->addLayout(headerHLayout, 0, 1);
|
|
layout->addLayout(m_optionsStack, 1, 1);
|
|
|
|
connect(m_optionsList, SIGNAL(currentRowChanged(int)), m_optionsStack, SLOT(setCurrentIndex(int)));
|
|
connect(m_optionsList, SIGNAL(currentTextChanged(const QString&)), m_headerLabel, SLOT(setText(const QString&)));
|
|
|
|
connect(this, SIGNAL(rejected()), this, SLOT(onRejected()));
|
|
}
|
|
|
|
QIcon getIcon(OptionsCategory *category)
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_assets = settings->paths.directory_assets;
|
|
QPixmap pixmap = QPixmap(QString(path_dir_assets)
|
|
+ "/xmb/monochrome/png/"
|
|
+ category->categoryIconName()
|
|
+ ".png");
|
|
return QIcon(getColorizedPixmap(pixmap, getLabelColor("iconColor")));
|
|
}
|
|
|
|
void ViewOptionsDialog::addCategory(OptionsCategory *category)
|
|
{
|
|
QTabWidget *tabWidget = new QTabWidget();
|
|
|
|
m_categoryList.append(category);
|
|
|
|
for (OptionsPage* page : category->pages())
|
|
{
|
|
SmartScrollArea *scrollArea = new SmartScrollArea(this);
|
|
QWidget *widget = page->widget();
|
|
|
|
scrollArea->setWidget(widget);
|
|
widget->setAutoFillBackground(false);
|
|
tabWidget->addTab(scrollArea, page->displayName());
|
|
}
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
|
|
tabWidget->setTabBarAutoHide(true);
|
|
#else
|
|
/* TODO remove the tabBar's space */
|
|
if (tabWidget->count() < 2)
|
|
tabWidget->tabBar()->hide();
|
|
#endif
|
|
m_optionsList->addItem(
|
|
new QListWidgetItem(getIcon(category),
|
|
category->displayName()));
|
|
m_optionsStack->addWidget(tabWidget);
|
|
}
|
|
|
|
void ViewOptionsDialog::repaintIcons()
|
|
{
|
|
unsigned i;
|
|
size_t list_size = (size_t)m_categoryList.size();
|
|
|
|
for (i = 0; i < list_size; i++)
|
|
m_optionsList->item(i)->setIcon(getIcon(m_categoryList.at(i)));
|
|
}
|
|
#else
|
|
ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
|
|
QDialog(mainwindow)
|
|
, m_viewOptionsWidget(new ViewOptionsWidget(mainwindow))
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
|
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
|
|
|
connect(this, SIGNAL(accepted()), m_viewOptionsWidget, SLOT(onAccepted()));
|
|
connect(this, SIGNAL(rejected()), m_viewOptionsWidget, SLOT(onRejected()));
|
|
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE));
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
layout->addWidget(m_viewOptionsWidget);
|
|
layout->addWidget(buttonBox);
|
|
|
|
setLayout(layout);
|
|
}
|
|
#endif
|
|
|
|
void ViewOptionsDialog::showDialog()
|
|
{
|
|
#ifdef HAVE_MENU
|
|
unsigned i;
|
|
size_t list_size = (size_t)m_categoryList.size();
|
|
for (i = 0; i < list_size; i++)
|
|
m_categoryList.at(i)->load();
|
|
#else
|
|
m_viewOptionsWidget->loadViewOptions();
|
|
#endif
|
|
show();
|
|
activateWindow();
|
|
}
|
|
|
|
void ViewOptionsDialog::hideDialog()
|
|
{
|
|
reject();
|
|
}
|
|
|
|
void ViewOptionsDialog::onRejected()
|
|
{
|
|
#ifdef HAVE_MENU
|
|
unsigned i;
|
|
size_t list_size = (size_t)m_categoryList.size();
|
|
for (i = 0; i < list_size; i++)
|
|
m_categoryList.at(i)->apply();
|
|
#endif
|
|
}
|
|
|
|
ViewOptionsWidget::ViewOptionsWidget(MainWindow *mainwindow, QWidget *parent) :
|
|
QWidget(parent)
|
|
,m_mainwindow(mainwindow)
|
|
,m_settings(mainwindow->settings())
|
|
,m_saveGeometryCheckBox(new QCheckBox(this))
|
|
,m_saveDockPositionsCheckBox(new QCheckBox(this))
|
|
,m_saveLastTabCheckBox(new QCheckBox(this))
|
|
,m_showHiddenFilesCheckBox(new QCheckBox(this))
|
|
,m_themeComboBox(new QComboBox(this))
|
|
,m_thumbnailCacheSpinBox(new QSpinBox(this))
|
|
,m_thumbnailDropSizeSpinBox(new QSpinBox(this))
|
|
,m_startupPlaylistComboBox(new QComboBox(this))
|
|
,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this))
|
|
,m_highlightColor()
|
|
,m_highlightColorLabel(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR), this))
|
|
,m_customThemePath()
|
|
,m_suggestLoadedCoreFirstCheckBox(new QCheckBox(this))
|
|
/* ,m_allPlaylistsListMaxCountSpinBox(new QSpinBox(this)) */
|
|
/* ,m_allPlaylistsGridMaxCountSpinBox(new QSpinBox(this)) */
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout;
|
|
QFormLayout *form = new QFormLayout;
|
|
|
|
m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT), MainWindow::THEME_SYSTEM_DEFAULT);
|
|
m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK);
|
|
m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM);
|
|
|
|
m_thumbnailCacheSpinBox->setSuffix(" MB");
|
|
m_thumbnailCacheSpinBox->setRange(0, 99999);
|
|
|
|
m_thumbnailDropSizeSpinBox->setSuffix(" px");
|
|
m_thumbnailDropSizeSpinBox->setRange(0, 99999);
|
|
|
|
/* m_allPlaylistsListMaxCountSpinBox->setRange(0, 99999); */
|
|
/* m_allPlaylistsGridMaxCountSpinBox->setRange(0, 99999); */
|
|
|
|
form->setFormAlignment(Qt::AlignCenter);
|
|
form->setLabelAlignment(Qt::AlignCenter);
|
|
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY), m_saveGeometryCheckBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS), m_saveDockPositionsCheckBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB), m_saveLastTabCheckBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SHOW_HIDDEN_FILES), m_showHiddenFilesCheckBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST), m_suggestLoadedCoreFirstCheckBox);
|
|
#if 0
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT), m_allPlaylistsListMaxCountSpinBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT), m_allPlaylistsGridMaxCountSpinBox);
|
|
#endif
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST), m_startupPlaylistComboBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT), m_thumbnailCacheSpinBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_DROP_SIZE_LIMIT), m_thumbnailDropSizeSpinBox);
|
|
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox);
|
|
form->addRow(m_highlightColorLabel, m_highlightColorPushButton);
|
|
|
|
layout->addLayout(form);
|
|
|
|
layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
|
|
setLayout(layout);
|
|
|
|
loadViewOptions();
|
|
|
|
connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
|
|
connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose()));
|
|
}
|
|
|
|
void ViewOptionsWidget::onThemeComboBoxIndexChanged(int)
|
|
{
|
|
MainWindow::Theme theme = static_cast<MainWindow::Theme>(m_themeComboBox->currentData(Qt::UserRole).toInt());
|
|
|
|
if (theme == MainWindow::THEME_CUSTOM)
|
|
{
|
|
QString filePath = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME));
|
|
|
|
if (filePath.isEmpty())
|
|
{
|
|
int oldThemeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString()));
|
|
|
|
if (m_themeComboBox->count() > oldThemeIndex)
|
|
{
|
|
disconnect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
|
|
m_themeComboBox->setCurrentIndex(oldThemeIndex);
|
|
connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_customThemePath = filePath;
|
|
|
|
if (m_mainwindow->setCustomThemeFile(filePath))
|
|
m_mainwindow->setTheme(theme);
|
|
}
|
|
}
|
|
else
|
|
m_mainwindow->setTheme(theme);
|
|
|
|
showOrHideHighlightColor();
|
|
}
|
|
|
|
void ViewOptionsWidget::onHighlightColorChoose()
|
|
{
|
|
QPixmap highlightPixmap(m_highlightColorPushButton->iconSize());
|
|
QColor currentHighlightColor = m_settings->value("highlight_color",
|
|
QApplication::palette().highlight().color()).value<QColor>();
|
|
QColor newHighlightColor = QColorDialog::getColor(
|
|
currentHighlightColor, this,
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR));
|
|
|
|
if (newHighlightColor.isValid())
|
|
{
|
|
MainWindow::Theme theme = static_cast<MainWindow::Theme>(
|
|
m_themeComboBox->currentData(Qt::UserRole).toInt());
|
|
|
|
m_highlightColor = newHighlightColor;
|
|
m_settings->setValue("highlight_color", m_highlightColor);
|
|
highlightPixmap.fill(m_highlightColor);
|
|
m_highlightColorPushButton->setIcon(highlightPixmap);
|
|
m_mainwindow->setTheme(theme);
|
|
}
|
|
}
|
|
|
|
void ViewOptionsWidget::loadViewOptions()
|
|
{
|
|
int i;
|
|
int themeIndex = 0;
|
|
int playlistIndex = 0;
|
|
QColor highlightColor =
|
|
m_settings->value("highlight_color",
|
|
QApplication::palette().highlight().color()).value<QColor>();
|
|
QPixmap highlightPixmap(m_highlightColorPushButton->iconSize());
|
|
QVector<QPair<QString, QString> > playlists = m_mainwindow->getPlaylists();
|
|
QString initialPlaylist = m_settings->value("initial_playlist",
|
|
m_mainwindow->getSpecialPlaylistPath(
|
|
SPECIAL_PLAYLIST_HISTORY)).toString();
|
|
|
|
m_saveGeometryCheckBox->setChecked(m_settings->value("save_geometry", false).toBool());
|
|
m_saveDockPositionsCheckBox->setChecked(m_settings->value("save_dock_positions", false).toBool());
|
|
m_saveLastTabCheckBox->setChecked(m_settings->value("save_last_tab", false).toBool());
|
|
m_showHiddenFilesCheckBox->setChecked(m_settings->value("show_hidden_files", true).toBool());
|
|
m_suggestLoadedCoreFirstCheckBox->setChecked(m_settings->value("suggest_loaded_core_first", false).toBool());
|
|
#if 0
|
|
m_allPlaylistsListMaxCountSpinBox->setValue(m_settings->value("all_playlists_list_max_count", 0).toInt());
|
|
m_allPlaylistsGridMaxCountSpinBox->setValue(m_settings->value("all_playlists_grid_max_count", 5000).toInt());
|
|
#endif
|
|
m_thumbnailCacheSpinBox->setValue(m_settings->value("thumbnail_cache_limit", 512).toInt());
|
|
m_thumbnailDropSizeSpinBox->setValue(m_settings->value("thumbnail_max_size", 0).toInt());
|
|
|
|
themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString()));
|
|
|
|
if (m_themeComboBox->count() > themeIndex)
|
|
m_themeComboBox->setCurrentIndex(themeIndex);
|
|
|
|
if (highlightColor.isValid())
|
|
{
|
|
m_highlightColor = highlightColor;
|
|
highlightPixmap.fill(m_highlightColor);
|
|
m_highlightColorPushButton->setIcon(highlightPixmap);
|
|
}
|
|
|
|
showOrHideHighlightColor();
|
|
|
|
m_startupPlaylistComboBox->clear();
|
|
|
|
for (i = 0; i < playlists.count(); i++)
|
|
{
|
|
const QPair<QString, QString> &pair = playlists.at(i);
|
|
|
|
m_startupPlaylistComboBox->addItem(pair.first, pair.second);
|
|
}
|
|
|
|
playlistIndex = m_startupPlaylistComboBox->findData(
|
|
initialPlaylist, Qt::UserRole, Qt::MatchFixedString);
|
|
|
|
if (playlistIndex >= 0)
|
|
m_startupPlaylistComboBox->setCurrentIndex(playlistIndex);
|
|
}
|
|
|
|
void ViewOptionsWidget::showOrHideHighlightColor()
|
|
{
|
|
if (m_mainwindow->theme() == MainWindow::THEME_DARK)
|
|
{
|
|
m_highlightColorLabel->show();
|
|
m_highlightColorPushButton->show();
|
|
}
|
|
else
|
|
{
|
|
m_highlightColorLabel->hide();
|
|
m_highlightColorPushButton->hide();
|
|
}
|
|
}
|
|
|
|
void ViewOptionsWidget::saveViewOptions()
|
|
{
|
|
m_settings->setValue("save_geometry", m_saveGeometryCheckBox->isChecked());
|
|
m_settings->setValue("save_dock_positions", m_saveDockPositionsCheckBox->isChecked());
|
|
m_settings->setValue("save_last_tab", m_saveLastTabCheckBox->isChecked());
|
|
m_settings->setValue("theme", m_mainwindow->getThemeString(static_cast<MainWindow::Theme>(m_themeComboBox->currentData(Qt::UserRole).toInt())));
|
|
m_settings->setValue("show_hidden_files", m_showHiddenFilesCheckBox->isChecked());
|
|
m_settings->setValue("highlight_color", m_highlightColor);
|
|
m_settings->setValue("suggest_loaded_core_first", m_suggestLoadedCoreFirstCheckBox->isChecked());
|
|
#if 0
|
|
m_settings->setValue("all_playlists_list_max_count", m_allPlaylistsListMaxCountSpinBox->value());
|
|
m_settings->setValue("all_playlists_grid_max_count", m_allPlaylistsGridMaxCountSpinBox->value());
|
|
#endif
|
|
m_settings->setValue("initial_playlist", m_startupPlaylistComboBox->currentData(Qt::UserRole).toString());
|
|
m_settings->setValue("thumbnail_cache_limit", m_thumbnailCacheSpinBox->value());
|
|
m_settings->setValue("thumbnail_max_size", m_thumbnailDropSizeSpinBox->value());
|
|
|
|
if (!m_mainwindow->customThemeString().isEmpty())
|
|
m_settings->setValue("custom_theme", m_customThemePath);
|
|
|
|
#if 0
|
|
m_mainwindow->setAllPlaylistsListMaxCount(m_allPlaylistsListMaxCountSpinBox->value());
|
|
m_mainwindow->setAllPlaylistsGridMaxCount(m_allPlaylistsGridMaxCountSpinBox->value());
|
|
#endif
|
|
m_mainwindow->setThumbnailCacheLimit(m_thumbnailCacheSpinBox->value());
|
|
}
|
|
|
|
void ViewOptionsWidget::onAccepted()
|
|
{
|
|
saveViewOptions();
|
|
}
|
|
|
|
void ViewOptionsWidget::onRejected()
|
|
{
|
|
loadViewOptions();
|
|
}
|
|
|
|
CoreOptionsDialog::CoreOptionsDialog(QWidget *parent) :
|
|
QDialog(parent)
|
|
,m_layout()
|
|
,m_scrollArea()
|
|
{
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_OPTIONS));
|
|
setObjectName("coreOptionsDialog");
|
|
|
|
resize(720, 480);
|
|
|
|
QTimer::singleShot(0, this, SLOT(clearLayout()));
|
|
}
|
|
|
|
CoreOptionsDialog::~CoreOptionsDialog()
|
|
{
|
|
}
|
|
|
|
void CoreOptionsDialog::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QDialog::resizeEvent(event);
|
|
|
|
if (!m_scrollArea)
|
|
return;
|
|
|
|
m_scrollArea->resize(event->size());
|
|
|
|
emit resized(event->size());
|
|
}
|
|
|
|
void CoreOptionsDialog::closeEvent(QCloseEvent *event)
|
|
{
|
|
QDialog::closeEvent(event);
|
|
|
|
emit closed();
|
|
}
|
|
|
|
void CoreOptionsDialog::paintEvent(QPaintEvent *event)
|
|
{
|
|
QStyleOption o;
|
|
QPainter p;
|
|
o.initFrom(this);
|
|
p.begin(this);
|
|
style()->drawPrimitive(
|
|
QStyle::PE_Widget, &o, &p, this);
|
|
p.end();
|
|
|
|
QDialog::paintEvent(event);
|
|
}
|
|
|
|
void CoreOptionsDialog::clearLayout()
|
|
{
|
|
QWidget *widget = NULL;
|
|
|
|
if (m_scrollArea)
|
|
{
|
|
foreach (QObject *obj, children())
|
|
{
|
|
obj->deleteLater();
|
|
}
|
|
}
|
|
|
|
m_layout = new QVBoxLayout();
|
|
|
|
widget = new QWidget();
|
|
widget->setLayout(m_layout);
|
|
widget->setObjectName("coreOptionsWidget");
|
|
|
|
m_scrollArea = new QScrollArea();
|
|
|
|
m_scrollArea->setParent(this);
|
|
m_scrollArea->setWidgetResizable(true);
|
|
m_scrollArea->setWidget(widget);
|
|
m_scrollArea->setObjectName("coreOptionsScrollArea");
|
|
m_scrollArea->show();
|
|
}
|
|
|
|
void CoreOptionsDialog::reload()
|
|
{
|
|
buildLayout();
|
|
}
|
|
|
|
void CoreOptionsDialog::onSaveGameSpecificOptions()
|
|
{
|
|
#ifdef HAVE_MENU
|
|
struct menu_state *menu_st = menu_state_get_ptr();
|
|
#endif
|
|
if (!core_options_create_override(true))
|
|
QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE));
|
|
#ifdef HAVE_MENU
|
|
menu_st->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH
|
|
| MENU_ST_FLAG_PREVENT_POPULATE;
|
|
#endif
|
|
}
|
|
|
|
void CoreOptionsDialog::onSaveFolderSpecificOptions()
|
|
{
|
|
#ifdef HAVE_MENU
|
|
struct menu_state *menu_st = menu_state_get_ptr();
|
|
#endif
|
|
if (!core_options_create_override(false))
|
|
QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE));
|
|
#ifdef HAVE_MENU
|
|
menu_st->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH
|
|
| MENU_ST_FLAG_PREVENT_POPULATE;
|
|
#endif
|
|
}
|
|
|
|
void CoreOptionsDialog::onCoreOptionComboBoxCurrentIndexChanged(int index)
|
|
{
|
|
unsigned i, k;
|
|
QString key, val;
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
QComboBox *combo_box = qobject_cast<QComboBox*>(sender());
|
|
|
|
if (!combo_box)
|
|
return;
|
|
|
|
key = combo_box->itemData(index, Qt::UserRole).toString();
|
|
val = combo_box->itemText(index);
|
|
|
|
if (runloop_st->core_options)
|
|
{
|
|
core_option_manager_t *coreopts = NULL;
|
|
|
|
retroarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
|
|
|
|
if (coreopts)
|
|
{
|
|
size_t opts = runloop_st->core_options->size;
|
|
|
|
for (i = 0; i < opts; i++)
|
|
{
|
|
QString optKey;
|
|
struct core_option *option = static_cast<struct core_option*>(&coreopts->opts[i]);
|
|
|
|
if (!option)
|
|
continue;
|
|
|
|
optKey = option->key;
|
|
|
|
if (key == optKey)
|
|
{
|
|
for (k = 0; k < option->vals->size; k++)
|
|
{
|
|
QString str = option->vals->elems[k].data;
|
|
|
|
if (!str.isEmpty() && str == val)
|
|
core_option_manager_set_val(coreopts, i, k, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CoreOptionsDialog::buildLayout()
|
|
{
|
|
unsigned j, k;
|
|
QFormLayout *form = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
|
bool has_core_options = (runloop_st->core_options != NULL);
|
|
size_t opts = (runloop_st->core_options) ? runloop_st->core_options->size : 0;
|
|
|
|
clearLayout();
|
|
|
|
if (has_core_options)
|
|
{
|
|
core_option_manager_t *coreopts = NULL;
|
|
|
|
form = new QFormLayout();
|
|
|
|
if (settings->bools.game_specific_options)
|
|
{
|
|
QString contentLabel;
|
|
QString label;
|
|
rarch_system_info_t *sys_info = &runloop_st->system;
|
|
|
|
/* TODO/FIXME - why have this check here? sys_info is not used */
|
|
if (sys_info)
|
|
contentLabel = QFileInfo(path_get(RARCH_PATH_BASENAME)).completeBaseName();
|
|
|
|
if (!contentLabel.isEmpty())
|
|
{
|
|
uint32_t flags = runloop_st->flags;
|
|
if (flags & RUNLOOP_FLAG_GAME_OPTIONS_ACTIVE)
|
|
label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE);
|
|
else
|
|
label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE);
|
|
|
|
if (!label.isEmpty())
|
|
{
|
|
QHBoxLayout *gameOptionsLayout = new QHBoxLayout();
|
|
QPushButton *button = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SAVE), this);
|
|
|
|
connect(button, SIGNAL(clicked()), this, SLOT(onSaveGameSpecificOptions()));
|
|
|
|
gameOptionsLayout->addWidget(new QLabel(contentLabel, this));
|
|
gameOptionsLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
|
|
gameOptionsLayout->addWidget(button);
|
|
|
|
form->addRow(label, gameOptionsLayout);
|
|
}
|
|
}
|
|
}
|
|
|
|
retroarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
|
|
|
|
if (coreopts)
|
|
{
|
|
QToolButton *resetAllButton = new QToolButton(this);
|
|
|
|
resetAllButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_ALL), this));
|
|
connect(resetAllButton, SIGNAL(clicked()), this, SLOT(onCoreOptionResetAllClicked()));
|
|
|
|
for (j = 0; j < opts; j++)
|
|
{
|
|
QString desc =
|
|
core_option_manager_get_desc(coreopts, j, false);
|
|
QString val =
|
|
core_option_manager_get_val(coreopts, j);
|
|
QComboBox *combo_box = NULL;
|
|
QLabel *descLabel = NULL;
|
|
QHBoxLayout *comboLayout = NULL;
|
|
QToolButton *resetButton = NULL;
|
|
struct core_option *option = NULL;
|
|
|
|
if (desc.isEmpty() || !coreopts->opts)
|
|
continue;
|
|
|
|
option = static_cast<struct core_option*>(&coreopts->opts[j]);
|
|
|
|
if (!option->vals || option->vals->size == 0)
|
|
continue;
|
|
|
|
comboLayout = new QHBoxLayout();
|
|
descLabel = new QLabel(desc, this);
|
|
combo_box = new QComboBox(this);
|
|
combo_box->setObjectName("coreOptionComboBox");
|
|
resetButton = new QToolButton(this);
|
|
resetButton->setObjectName("resetButton");
|
|
resetButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET), this));
|
|
resetButton->setProperty("comboBox",
|
|
QVariant::fromValue(combo_box));
|
|
|
|
connect(resetButton, SIGNAL(clicked()),
|
|
this, SLOT(onCoreOptionResetClicked()));
|
|
|
|
if (!string_is_empty(option->info))
|
|
{
|
|
char *new_info;
|
|
size_t option_info_len = strlen(option->info);
|
|
size_t new_info_len = option_info_len + 1;
|
|
|
|
if (!(new_info = (char *)malloc(new_info_len)))
|
|
return;
|
|
new_info[0] = '\0';
|
|
|
|
word_wrap(new_info, new_info_len, option->info,
|
|
option_info_len, 50, 100, 0);
|
|
descLabel->setToolTip(new_info);
|
|
combo_box->setToolTip(new_info);
|
|
free(new_info);
|
|
}
|
|
|
|
for (k = 0; k < option->vals->size; k++)
|
|
combo_box->addItem(option->vals->elems[k].data, QVariant::fromValue(QString(option->key)));
|
|
|
|
combo_box->setCurrentText(val);
|
|
combo_box->setProperty("default_index",
|
|
static_cast<unsigned>(option->default_index));
|
|
|
|
/* Only connect the signal after setting the default item */
|
|
connect(combo_box, SIGNAL(currentIndexChanged(int)),
|
|
this,
|
|
SLOT(onCoreOptionComboBoxCurrentIndexChanged(int)));
|
|
|
|
comboLayout->addWidget(combo_box);
|
|
comboLayout->addWidget(resetButton);
|
|
|
|
form->addRow(descLabel, comboLayout);
|
|
}
|
|
|
|
form->addRow(resetAllButton, new QWidget(this));
|
|
|
|
m_layout->addLayout(form);
|
|
}
|
|
}
|
|
|
|
if (!opts)
|
|
{
|
|
QLabel *noParamsLabel = new QLabel(msg_hash_to_str(
|
|
MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE), this);
|
|
noParamsLabel->setAlignment(Qt::AlignCenter);
|
|
|
|
m_layout->addWidget(noParamsLabel);
|
|
}
|
|
|
|
m_layout->addItem(new QSpacerItem(20, 20,
|
|
QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
|
|
resize(width() + 1, height());
|
|
show();
|
|
resize(width() - 1, height());
|
|
}
|
|
|
|
void CoreOptionsDialog::onCoreOptionResetClicked()
|
|
{
|
|
bool ok = false;
|
|
QToolButton *button = qobject_cast<QToolButton*>(sender());
|
|
QComboBox *combo_box = NULL;
|
|
int default_index = 0;
|
|
|
|
if (!button)
|
|
return;
|
|
|
|
combo_box = qobject_cast<QComboBox*>(button->property("comboBox").value<QComboBox*>());
|
|
|
|
if (!combo_box)
|
|
return;
|
|
|
|
default_index = combo_box->property("default_index").toInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
if (default_index >= 0 && default_index < combo_box->count())
|
|
combo_box->setCurrentIndex(default_index);
|
|
}
|
|
|
|
void CoreOptionsDialog::onCoreOptionResetAllClicked()
|
|
{
|
|
int i;
|
|
QList<QComboBox*> combo_boxes = findChildren<QComboBox*>("coreOptionComboBox");
|
|
|
|
for (i = 0; i < combo_boxes.count(); i++)
|
|
{
|
|
int default_index = 0;
|
|
bool ok = false;
|
|
QComboBox *combo_box = combo_boxes.at(i);
|
|
|
|
if (!combo_box)
|
|
continue;
|
|
|
|
default_index = combo_box->property("default_index").toInt(&ok);
|
|
|
|
if (!ok)
|
|
continue;
|
|
|
|
if (default_index >= 0 && default_index < combo_box->count())
|
|
combo_box->setCurrentIndex(default_index);
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_MENU)
|
|
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
|
|
enum
|
|
{
|
|
QT_SHADER_PRESET_GLOBAL = 0,
|
|
QT_SHADER_PRESET_CORE,
|
|
QT_SHADER_PRESET_PARENT,
|
|
QT_SHADER_PRESET_GAME,
|
|
QT_SHADER_PRESET_NORMAL
|
|
};
|
|
|
|
ShaderPass::ShaderPass(struct video_shader_pass *passToCopy) :
|
|
pass(NULL)
|
|
{
|
|
if (passToCopy)
|
|
{
|
|
pass = (struct video_shader_pass*)calloc(1, sizeof(*pass));
|
|
memcpy(pass, passToCopy, sizeof(*pass));
|
|
}
|
|
}
|
|
|
|
ShaderPass::~ShaderPass()
|
|
{
|
|
if (pass)
|
|
free(pass);
|
|
}
|
|
|
|
ShaderPass& ShaderPass::operator=(const ShaderPass &other)
|
|
{
|
|
if (this != &other && other.pass)
|
|
{
|
|
pass = (struct video_shader_pass*)calloc(1, sizeof(*pass));
|
|
memcpy(pass, other.pass, sizeof(*pass));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
ShaderParamsDialog::ShaderParamsDialog(QWidget *parent) :
|
|
QDialog(parent)
|
|
,m_layout()
|
|
,m_scrollArea()
|
|
{
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
|
|
setObjectName("shaderParamsDialog");
|
|
|
|
resize(720, 480);
|
|
|
|
QTimer::singleShot(0, this, SLOT(clearLayout()));
|
|
}
|
|
|
|
ShaderParamsDialog::~ShaderParamsDialog()
|
|
{
|
|
}
|
|
|
|
void ShaderParamsDialog::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QDialog::resizeEvent(event);
|
|
|
|
if (!m_scrollArea)
|
|
return;
|
|
|
|
m_scrollArea->resize(event->size());
|
|
|
|
emit resized(event->size());
|
|
}
|
|
|
|
void ShaderParamsDialog::closeEvent(QCloseEvent *event)
|
|
{
|
|
QDialog::closeEvent(event);
|
|
|
|
emit closed();
|
|
}
|
|
|
|
void ShaderParamsDialog::paintEvent(QPaintEvent *event)
|
|
{
|
|
QStyleOption o;
|
|
QPainter p;
|
|
o.initFrom(this);
|
|
p.begin(this);
|
|
style()->drawPrimitive(
|
|
QStyle::PE_Widget, &o, &p, this);
|
|
p.end();
|
|
|
|
QDialog::paintEvent(event);
|
|
}
|
|
|
|
QString ShaderParamsDialog::getFilterLabel(unsigned filter)
|
|
{
|
|
QString filterString;
|
|
|
|
switch (filter)
|
|
{
|
|
case 0:
|
|
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
|
|
break;
|
|
case 1:
|
|
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LINEAR);
|
|
break;
|
|
case 2:
|
|
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NEAREST);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return filterString;
|
|
}
|
|
|
|
void ShaderParamsDialog::clearLayout()
|
|
{
|
|
QWidget *widget = NULL;
|
|
|
|
if (m_scrollArea)
|
|
{
|
|
foreach (QObject *obj, children())
|
|
delete obj;
|
|
}
|
|
|
|
m_layout = new QVBoxLayout();
|
|
|
|
widget = new QWidget();
|
|
widget->setLayout(m_layout);
|
|
widget->setObjectName("shaderParamsWidget");
|
|
|
|
m_scrollArea = new QScrollArea();
|
|
|
|
m_scrollArea->setParent(this);
|
|
m_scrollArea->setWidgetResizable(true);
|
|
m_scrollArea->setWidget(widget);
|
|
m_scrollArea->setObjectName("shaderParamsScrollArea");
|
|
m_scrollArea->show();
|
|
}
|
|
|
|
void ShaderParamsDialog::getShaders(struct video_shader **menu_shader, struct video_shader **video_shader)
|
|
{
|
|
video_shader_ctx_t shader_info = {0};
|
|
struct video_shader *shader = menu_shader_get();
|
|
|
|
if (menu_shader)
|
|
{
|
|
if (shader)
|
|
*menu_shader = shader;
|
|
else
|
|
*menu_shader = NULL;
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
if (shader)
|
|
*video_shader = shader_info.data;
|
|
else
|
|
*video_shader = NULL;
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
if (!video_shader_driver_get_current_shader(&shader_info))
|
|
{
|
|
*video_shader = NULL;
|
|
return;
|
|
}
|
|
|
|
if (!shader_info.data || shader_info.data->num_parameters > GFX_MAX_PARAMETERS)
|
|
{
|
|
*video_shader = NULL;
|
|
return;
|
|
}
|
|
|
|
if (shader_info.data)
|
|
*video_shader = shader_info.data;
|
|
else
|
|
*video_shader = NULL;
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onFilterComboBoxIndexChanged(int)
|
|
{
|
|
QVariant passVariant;
|
|
QComboBox *comboBox = qobject_cast<QComboBox*>(sender());
|
|
int pass = 0;
|
|
bool ok = false;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!comboBox)
|
|
return;
|
|
|
|
passVariant = comboBox->property("pass");
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
if ( menu_shader
|
|
&& (pass >= 0)
|
|
&& (pass < static_cast<int>(menu_shader->passes)))
|
|
{
|
|
QVariant data = comboBox->currentData();
|
|
|
|
if (data.isValid())
|
|
{
|
|
unsigned filter = data.toUInt(&ok);
|
|
|
|
if (ok)
|
|
{
|
|
if (menu_shader)
|
|
menu_shader->pass[pass].filter = filter;
|
|
if (video_shader)
|
|
video_shader->pass[pass].filter = filter;
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onScaleComboBoxIndexChanged(int)
|
|
{
|
|
QVariant passVariant;
|
|
QComboBox *comboBox = qobject_cast<QComboBox*>(sender());
|
|
int pass = 0;
|
|
bool ok = false;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!comboBox)
|
|
return;
|
|
|
|
passVariant = comboBox->property("pass");
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
if (menu_shader && pass >= 0 && pass < static_cast<int>(menu_shader->passes))
|
|
{
|
|
QVariant data = comboBox->currentData();
|
|
|
|
if (data.isValid())
|
|
{
|
|
unsigned scale = data.toUInt(&ok);
|
|
|
|
if (ok)
|
|
{
|
|
if (menu_shader)
|
|
{
|
|
menu_shader->pass[pass].fbo.scale_x = scale;
|
|
menu_shader->pass[pass].fbo.scale_y = scale;
|
|
if (scale)
|
|
menu_shader->pass[pass].fbo.flags |= FBO_SCALE_FLAG_VALID;
|
|
else
|
|
menu_shader->pass[pass].fbo.flags &= ~FBO_SCALE_FLAG_VALID;
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
video_shader->pass[pass].fbo.scale_x = scale;
|
|
video_shader->pass[pass].fbo.scale_y = scale;
|
|
if (scale)
|
|
video_shader->pass[pass].fbo.flags |= FBO_SCALE_FLAG_VALID;
|
|
else
|
|
video_shader->pass[pass].fbo.flags &= ~FBO_SCALE_FLAG_VALID;
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderPassMoveDownClicked()
|
|
{
|
|
QVariant passVariant;
|
|
bool ok = false;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
QToolButton *button = qobject_cast<QToolButton*>(sender());
|
|
int pass = 0;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!button)
|
|
return;
|
|
|
|
passVariant = button->property("pass");
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok || pass < 0)
|
|
return;
|
|
|
|
if (video_shader)
|
|
{
|
|
ShaderPass tempPass;
|
|
int i;
|
|
|
|
if (pass >= static_cast<int>(video_shader->passes) - 1)
|
|
return;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
struct video_shader_parameter *param = &video_shader->parameters[i];
|
|
|
|
if (param->pass == pass)
|
|
param->pass += 1;
|
|
else if (param->pass == pass + 1)
|
|
param->pass -= 1;
|
|
}
|
|
|
|
tempPass = ShaderPass(&video_shader->pass[pass]);
|
|
memcpy(&video_shader->pass[pass], &video_shader->pass[pass + 1], sizeof(struct video_shader_pass));
|
|
memcpy(&video_shader->pass[pass + 1], tempPass.pass, sizeof(struct video_shader_pass));
|
|
}
|
|
|
|
if (menu_shader)
|
|
{
|
|
ShaderPass tempPass;
|
|
int i;
|
|
|
|
if (pass >= static_cast<int>(menu_shader->passes) - 1)
|
|
return;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
struct video_shader_parameter *param = &menu_shader->parameters[i];
|
|
|
|
if (param->pass == pass)
|
|
param->pass += 1;
|
|
else if (param->pass == pass + 1)
|
|
param->pass -= 1;
|
|
}
|
|
|
|
tempPass = ShaderPass(&menu_shader->pass[pass]);
|
|
memcpy(&menu_shader->pass[pass], &menu_shader->pass[pass + 1], sizeof(struct video_shader_pass));
|
|
memcpy(&menu_shader->pass[pass + 1], tempPass.pass, sizeof(struct video_shader_pass));
|
|
}
|
|
|
|
menu_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
reload();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderPassMoveUpClicked()
|
|
{
|
|
QVariant passVariant;
|
|
int pass = 0;
|
|
bool ok = false;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
QToolButton *button = qobject_cast<QToolButton*>(sender());
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!button)
|
|
return;
|
|
|
|
passVariant = button->property("pass");
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok || pass <= 0)
|
|
return;
|
|
|
|
if (video_shader)
|
|
{
|
|
ShaderPass tempPass;
|
|
int i;
|
|
|
|
if (pass > static_cast<int>(video_shader->passes) - 1)
|
|
return;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
struct video_shader_parameter *param = &video_shader->parameters[i];
|
|
|
|
if (param->pass == pass)
|
|
param->pass -= 1;
|
|
else if (param->pass == pass - 1)
|
|
param->pass += 1;
|
|
}
|
|
|
|
tempPass = ShaderPass(&video_shader->pass[pass - 1]);
|
|
memcpy(&video_shader->pass[pass - 1], &video_shader->pass[pass], sizeof(struct video_shader_pass));
|
|
memcpy(&video_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass));
|
|
}
|
|
|
|
if (menu_shader)
|
|
{
|
|
ShaderPass tempPass;
|
|
int i;
|
|
|
|
if (pass > static_cast<int>(menu_shader->passes) - 1)
|
|
return;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
struct video_shader_parameter *param = &menu_shader->parameters[i];
|
|
|
|
if (param->pass == pass)
|
|
param->pass -= 1;
|
|
else if (param->pass == pass - 1)
|
|
param->pass += 1;
|
|
}
|
|
|
|
tempPass = ShaderPass(&menu_shader->pass[pass - 1]);
|
|
memcpy(&menu_shader->pass[pass - 1], &menu_shader->pass[pass], sizeof(struct video_shader_pass));
|
|
memcpy(&menu_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass));
|
|
}
|
|
|
|
menu_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
reload();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderLoadPresetClicked()
|
|
{
|
|
QString path, filter;
|
|
QByteArray pathArray;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
const char *pathData = NULL;
|
|
enum rarch_shader_type type = RARCH_SHADER_NONE;
|
|
#if !defined(HAVE_MENU)
|
|
settings_t *settings = config_get_ptr();
|
|
const char *shader_preset_dir = settings->paths.directory_video_shader;
|
|
#else
|
|
const char *shader_preset_dir = NULL;
|
|
|
|
menu_driver_get_last_shader_preset_path(&shader_preset_dir, NULL);
|
|
#endif
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!menu_shader)
|
|
return;
|
|
|
|
filter.append("Shader Preset (");
|
|
|
|
/* NOTE: Maybe we should have a way to get a list
|
|
* of all shader types instead of hard-coding this? */
|
|
if (video_shader_is_supported(RARCH_SHADER_CG))
|
|
{
|
|
filter.append(QLatin1String(" *"));
|
|
filter.append(".cgp");
|
|
}
|
|
|
|
if (video_shader_is_supported(RARCH_SHADER_GLSL))
|
|
{
|
|
filter.append(QLatin1String(" *"));
|
|
filter.append(".glslp");
|
|
}
|
|
|
|
if (video_shader_is_supported(RARCH_SHADER_SLANG))
|
|
{
|
|
filter.append(QLatin1String(" *"));
|
|
filter.append(".slangp");
|
|
}
|
|
|
|
filter.append(")");
|
|
path = QFileDialog::getOpenFileName(
|
|
this,
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET),
|
|
shader_preset_dir, filter);
|
|
|
|
if (path.isEmpty())
|
|
return;
|
|
|
|
pathArray = path.toUtf8();
|
|
pathData = pathArray.constData();
|
|
type = video_shader_parse_type(pathData);
|
|
|
|
#if defined(HAVE_MENU)
|
|
/* Cache selected shader parent directory */
|
|
menu_driver_set_last_shader_preset_path(pathData);
|
|
#endif
|
|
|
|
menu_shader_manager_set_preset(menu_shader, type, pathData, true);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderResetPass(int pass)
|
|
{
|
|
unsigned i;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (menu_shader)
|
|
{
|
|
for (i = 0; i < menu_shader->num_parameters; i++)
|
|
{
|
|
struct video_shader_parameter *param = &menu_shader->parameters[i];
|
|
|
|
/* if pass < 0, reset all params,
|
|
* otherwise only reset the selected pass */
|
|
if (pass >= 0 && param->pass != pass)
|
|
continue;
|
|
|
|
param->current = param->initial;
|
|
}
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
for (i = 0; i < video_shader->num_parameters; i++)
|
|
{
|
|
struct video_shader_parameter *param = &video_shader->parameters[i];
|
|
|
|
/* if pass < 0, reset all params,
|
|
* otherwise only reset the selected pass */
|
|
if (pass >= 0 && param->pass != pass)
|
|
continue;
|
|
|
|
param->current = param->initial;
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
|
|
reload();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderResetParameter(QString parameter)
|
|
{
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (menu_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
QString id = menu_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &menu_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
param->current = param->initial;
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
QString id = video_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &video_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
param->current = param->initial;
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
|
|
reload();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderResetAllPasses()
|
|
{
|
|
onShaderResetPass(-1);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderAddPassClicked()
|
|
{
|
|
QString path, filter;
|
|
QByteArray pathArray;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
struct video_shader_pass *shader_pass = NULL;
|
|
const char *pathData = NULL;
|
|
#if !defined(HAVE_MENU)
|
|
settings_t *settings = config_get_ptr();
|
|
const char *shader_pass_dir = settings->paths.directory_video_shader;
|
|
#else
|
|
const char *shader_pass_dir = NULL;
|
|
|
|
menu_driver_get_last_shader_pass_path(&shader_pass_dir, NULL);
|
|
#endif
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!menu_shader)
|
|
return;
|
|
|
|
filter.append("Shader (");
|
|
|
|
/* NOTE: Maybe we should have a way to get a list
|
|
* of all shader types instead of hard-coding this? */
|
|
if (video_shader_is_supported(RARCH_SHADER_CG))
|
|
filter.append(QLatin1String(" *.cg"));
|
|
|
|
if (video_shader_is_supported(RARCH_SHADER_GLSL))
|
|
filter.append(QLatin1String(" *.glsl"));
|
|
|
|
if (video_shader_is_supported(RARCH_SHADER_SLANG))
|
|
filter.append(QLatin1String(" *.slang"));
|
|
|
|
filter.append(")");
|
|
|
|
path = QFileDialog::getOpenFileName(
|
|
this,
|
|
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET),
|
|
shader_pass_dir, filter);
|
|
|
|
if (path.isEmpty())
|
|
return;
|
|
|
|
/* Qt uses '/' as a directory separator regardless
|
|
* of host platform. Have to convert to native separators,
|
|
* or video_shader_resolve_parameters() will fail on
|
|
* non-Linux platforms */
|
|
path = QDir::toNativeSeparators(path);
|
|
|
|
pathArray = path.toUtf8();
|
|
pathData = pathArray.constData();
|
|
|
|
if (menu_shader->passes < GFX_MAX_SHADERS)
|
|
menu_shader->passes++;
|
|
else
|
|
return;
|
|
|
|
menu_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
shader_pass = &menu_shader->pass[menu_shader->passes - 1];
|
|
|
|
if (!shader_pass)
|
|
return;
|
|
|
|
strlcpy(shader_pass->source.path,
|
|
pathData,
|
|
sizeof(shader_pass->source.path));
|
|
|
|
#if defined(HAVE_MENU)
|
|
/* Cache selected shader parent directory */
|
|
menu_driver_set_last_shader_pass_path(pathData);
|
|
#endif
|
|
|
|
video_shader_resolve_parameters(menu_shader);
|
|
|
|
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderSavePresetAsClicked()
|
|
{
|
|
QByteArray pathArray;
|
|
const char *pathData = NULL;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_video_shader = settings->paths.directory_video_shader;
|
|
QString path = QFileDialog::getSaveFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS), path_dir_video_shader);
|
|
|
|
if (path.isEmpty())
|
|
return;
|
|
|
|
pathArray = path.toUtf8();
|
|
pathData = pathArray.constData();
|
|
|
|
operateShaderPreset(true, pathData, QT_SHADER_PRESET_NORMAL);
|
|
}
|
|
|
|
/** save or remove shader preset */
|
|
void ShaderParamsDialog::operateShaderPreset(bool save, const char *path, unsigned action_type)
|
|
{
|
|
bool ret;
|
|
enum auto_shader_type preset_type;
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_video_shader = settings->paths.directory_video_shader;
|
|
const char *path_dir_menu_config = settings->paths.directory_menu_config;
|
|
|
|
switch (action_type)
|
|
{
|
|
case QT_SHADER_PRESET_GLOBAL:
|
|
preset_type = SHADER_PRESET_GLOBAL;
|
|
break;
|
|
case QT_SHADER_PRESET_CORE:
|
|
preset_type = SHADER_PRESET_CORE;
|
|
break;
|
|
case QT_SHADER_PRESET_PARENT:
|
|
preset_type = SHADER_PRESET_PARENT;
|
|
break;
|
|
case QT_SHADER_PRESET_GAME:
|
|
preset_type = SHADER_PRESET_GAME;
|
|
break;
|
|
case QT_SHADER_PRESET_NORMAL:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (save)
|
|
{
|
|
if (action_type == QT_SHADER_PRESET_NORMAL)
|
|
ret = menu_shader_manager_save_preset(
|
|
menu_shader_get(),
|
|
path,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config,
|
|
true);
|
|
else
|
|
ret = menu_shader_manager_save_auto_preset(
|
|
menu_shader_get(),
|
|
preset_type,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config,
|
|
true);
|
|
|
|
if (ret)
|
|
runloop_msg_queue_push(
|
|
msg_hash_to_str(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY),
|
|
1, 100, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT,
|
|
MESSAGE_QUEUE_CATEGORY_INFO
|
|
);
|
|
else
|
|
runloop_msg_queue_push(
|
|
msg_hash_to_str(MSG_ERROR_SAVING_SHADER_PRESET),
|
|
1, 100, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT,
|
|
MESSAGE_QUEUE_CATEGORY_ERROR
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (action_type != QT_SHADER_PRESET_NORMAL &&
|
|
menu_shader_manager_remove_auto_preset(preset_type,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config))
|
|
{
|
|
runloop_msg_queue_push(
|
|
msg_hash_to_str(MSG_SHADER_PRESET_REMOVED_SUCCESSFULLY),
|
|
1, 100, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT,
|
|
MESSAGE_QUEUE_CATEGORY_INFO
|
|
);
|
|
|
|
#ifdef HAVE_MENU
|
|
menu_state_get_ptr()->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH;
|
|
#endif
|
|
}
|
|
else
|
|
runloop_msg_queue_push(
|
|
msg_hash_to_str(MSG_ERROR_REMOVING_SHADER_PRESET),
|
|
1, 100, true, NULL,
|
|
MESSAGE_QUEUE_ICON_DEFAULT,
|
|
MESSAGE_QUEUE_CATEGORY_ERROR
|
|
);
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderSaveGlobalPresetClicked()
|
|
{
|
|
operateShaderPreset(true, NULL, QT_SHADER_PRESET_GLOBAL);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderSaveCorePresetClicked()
|
|
{
|
|
operateShaderPreset(true, NULL, QT_SHADER_PRESET_CORE);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderSaveParentPresetClicked()
|
|
{
|
|
operateShaderPreset(true, NULL, QT_SHADER_PRESET_PARENT);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderSaveGamePresetClicked()
|
|
{
|
|
operateShaderPreset(true, NULL, QT_SHADER_PRESET_GAME);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemoveGlobalPresetClicked()
|
|
{
|
|
operateShaderPreset(false, NULL, QT_SHADER_PRESET_GLOBAL);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemoveCorePresetClicked()
|
|
{
|
|
operateShaderPreset(false, NULL, QT_SHADER_PRESET_CORE);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemoveParentPresetClicked()
|
|
{
|
|
operateShaderPreset(false, NULL, QT_SHADER_PRESET_PARENT);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemoveGamePresetClicked()
|
|
{
|
|
operateShaderPreset(false, NULL, QT_SHADER_PRESET_GAME);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemoveAllPassesClicked()
|
|
{
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!menu_shader)
|
|
return;
|
|
|
|
menu_shader->passes = 0;
|
|
menu_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
onShaderApplyClicked();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemovePassClicked()
|
|
{
|
|
QVariant passVariant;
|
|
QAction *action = qobject_cast<QAction*>(sender());
|
|
int pass = 0;
|
|
bool ok = false;
|
|
|
|
if (!action)
|
|
return;
|
|
passVariant = action->data();
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
onShaderRemovePass(pass);
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderRemovePass(int pass)
|
|
{
|
|
int i;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!menu_shader || menu_shader->passes == 0)
|
|
return;
|
|
|
|
if (pass < 0 || pass > static_cast<int>(menu_shader->passes))
|
|
return;
|
|
|
|
/* move selected pass to the bottom */
|
|
for (i = pass; i < static_cast<int>(menu_shader->passes) - 1; i++)
|
|
std::swap(menu_shader->pass[i], menu_shader->pass[i + 1]);
|
|
|
|
menu_shader->passes--;
|
|
|
|
menu_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
|
|
onShaderApplyClicked();
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderApplyClicked()
|
|
{
|
|
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
|
|
}
|
|
|
|
void ShaderParamsDialog::updateRemovePresetButtonsState()
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
const char *path_dir_video_shader = settings->paths.directory_video_shader;
|
|
const char *path_dir_menu_config = settings->paths.directory_menu_config;
|
|
|
|
if (removeGlobalPresetAction)
|
|
removeGlobalPresetAction->setEnabled(
|
|
menu_shader_manager_auto_preset_exists(
|
|
SHADER_PRESET_GLOBAL,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config
|
|
));
|
|
if (removeCorePresetAction)
|
|
removeCorePresetAction->setEnabled(
|
|
menu_shader_manager_auto_preset_exists(
|
|
SHADER_PRESET_CORE,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config
|
|
));
|
|
if (removeParentPresetAction)
|
|
removeParentPresetAction->setEnabled(
|
|
menu_shader_manager_auto_preset_exists(
|
|
SHADER_PRESET_PARENT,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config
|
|
));
|
|
if (removeGamePresetAction)
|
|
removeGamePresetAction->setEnabled(
|
|
menu_shader_manager_auto_preset_exists(
|
|
SHADER_PRESET_GAME,
|
|
path_dir_video_shader,
|
|
path_dir_menu_config
|
|
));
|
|
}
|
|
|
|
void ShaderParamsDialog::reload()
|
|
{
|
|
buildLayout();
|
|
}
|
|
|
|
void ShaderParamsDialog::buildLayout()
|
|
{
|
|
unsigned i;
|
|
bool hasPasses = false;
|
|
#if defined(HAVE_MENU)
|
|
CheckableSettingsGroup *topSettingsGroup = NULL;
|
|
#endif
|
|
QPushButton *loadButton = NULL;
|
|
QPushButton *saveButton = NULL;
|
|
QPushButton *removeButton = NULL;
|
|
QPushButton *removePassButton = NULL;
|
|
QPushButton *applyButton = NULL;
|
|
QHBoxLayout *topButtonLayout = NULL;
|
|
QMenu *loadMenu = NULL;
|
|
QMenu *saveMenu = NULL;
|
|
QMenu *removeMenu = NULL;
|
|
QMenu *removePassMenu = NULL;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
struct video_shader *avail_shader = NULL;
|
|
const char *shader_path = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
/* NOTE: For some reason, menu_shader_get() returns a COPY
|
|
* of what get_current_shader() gives us.
|
|
* And if you want to be able to change shader settings/parameters
|
|
* from both the raster menu and
|
|
* Qt at the same time... you must change BOTH or one will
|
|
* overwrite the other.
|
|
*
|
|
* AND, during a context reset, video_shader will be NULL
|
|
* but not menu_shader, so don't totally bail
|
|
* just because video_shader is NULL.
|
|
*
|
|
* Someone please fix this mess.
|
|
*/
|
|
|
|
if (video_shader)
|
|
{
|
|
avail_shader = video_shader;
|
|
|
|
if (video_shader->passes == 0)
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
|
|
}
|
|
/* Normally we'd only use video_shader,
|
|
* but the Vulkan driver returns a NULL shader when there
|
|
* are zero passes, so just fall back to menu_shader.
|
|
*/
|
|
else if (menu_shader)
|
|
{
|
|
avail_shader = menu_shader;
|
|
|
|
if (menu_shader->passes == 0)
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
|
|
}
|
|
else
|
|
{
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
|
|
|
|
/* no shader is available yet, just keep retrying until it is */
|
|
QTimer::singleShot(0, this, SLOT(buildLayout()));
|
|
return;
|
|
}
|
|
|
|
clearLayout();
|
|
|
|
/* Only check video_shader for the path, menu_shader seems stale...
|
|
* e.g. if you remove all the shader passes,
|
|
* it still has the old path in it, but video_shader does not
|
|
*/
|
|
if (video_shader)
|
|
{
|
|
if (!string_is_empty(video_shader->path))
|
|
{
|
|
shader_path = video_shader->path;
|
|
setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName());
|
|
}
|
|
}
|
|
else if (menu_shader)
|
|
{
|
|
if (!string_is_empty(menu_shader->path))
|
|
{
|
|
shader_path = menu_shader->path;
|
|
setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName());
|
|
}
|
|
}
|
|
else
|
|
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
|
|
|
|
loadButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD), this);
|
|
saveButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SAVE), this);
|
|
removeButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_REMOVE), this);
|
|
removePassButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_REMOVE_PASSES), this);
|
|
applyButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_APPLY), this);
|
|
|
|
loadMenu = new QMenu(loadButton);
|
|
|
|
loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET), this, SLOT(onShaderLoadPresetClicked()));
|
|
loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_ADD_PASS), this, SLOT(onShaderAddPassClicked()));
|
|
|
|
loadButton->setMenu(loadMenu);
|
|
|
|
saveMenu = new QMenu(saveButton);
|
|
saveMenu->addAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS)) + "...", this, SLOT(onShaderSavePresetAsClicked()));
|
|
saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GLOBAL), this, SLOT(onShaderSaveGlobalPresetClicked()));
|
|
saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE), this, SLOT(onShaderSaveCorePresetClicked()));
|
|
saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_PARENT), this, SLOT(onShaderSaveParentPresetClicked()));
|
|
saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME), this, SLOT(onShaderSaveGamePresetClicked()));
|
|
|
|
saveButton->setMenu(saveMenu);
|
|
|
|
removeMenu = new QMenu(removeButton);
|
|
removeGlobalPresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_GLOBAL), this, SLOT(onShaderRemoveGlobalPresetClicked()));
|
|
removeCorePresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_CORE), this, SLOT(onShaderRemoveCorePresetClicked()));
|
|
removeParentPresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_PARENT), this, SLOT(onShaderRemoveParentPresetClicked()));
|
|
removeGamePresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_GAME), this, SLOT(onShaderRemoveGamePresetClicked()));
|
|
|
|
removeButton->setMenu(removeMenu);
|
|
|
|
connect(removeMenu, SIGNAL(aboutToShow()), this, SLOT(updateRemovePresetButtonsState()));
|
|
|
|
removePassMenu = new QMenu(removeButton);
|
|
|
|
/* When there are no passes, at least on first startup, it seems video_shader erroneously shows 1 pass, with an empty source file.
|
|
* So we use menu_shader instead for that.
|
|
*/
|
|
if (menu_shader)
|
|
{
|
|
for (i = 0; i < menu_shader->passes; i++)
|
|
{
|
|
QFileInfo fileInfo(menu_shader->pass[i].source.path);
|
|
QString shaderBasename = fileInfo.completeBaseName();
|
|
QAction *action = removePassMenu->addAction(shaderBasename, this, SLOT(onShaderRemovePassClicked()));
|
|
|
|
action->setData(i);
|
|
}
|
|
}
|
|
|
|
removePassMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_CLEAR_ALL_PASSES), this, SLOT(onShaderRemoveAllPassesClicked()));
|
|
|
|
removePassButton->setMenu(removePassMenu);
|
|
|
|
connect(applyButton, SIGNAL(clicked()), this, SLOT(onShaderApplyClicked()));
|
|
|
|
#if defined(HAVE_MENU)
|
|
topSettingsGroup = new CheckableSettingsGroup(MENU_ENUM_LABEL_VIDEO_SHADERS_ENABLE);
|
|
topSettingsGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE);
|
|
topSettingsGroup->add(MENU_ENUM_LABEL_SHADER_WATCH_FOR_CHANGES);
|
|
topSettingsGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_REMEMBER_LAST_DIR);
|
|
#endif
|
|
|
|
topButtonLayout = new QHBoxLayout();
|
|
topButtonLayout->addWidget(loadButton);
|
|
topButtonLayout->addWidget(saveButton);
|
|
topButtonLayout->addWidget(removeButton);
|
|
topButtonLayout->addWidget(removePassButton);
|
|
topButtonLayout->addWidget(applyButton);
|
|
|
|
#if defined(HAVE_MENU)
|
|
m_layout->addWidget(topSettingsGroup);
|
|
#endif
|
|
m_layout->addLayout(topButtonLayout);
|
|
|
|
/* NOTE: We assume that parameters are always grouped in order by the pass number, e.g., all parameters for pass 0 come first, then params for pass 1, etc. */
|
|
for (i = 0; avail_shader && i < avail_shader->passes; i++)
|
|
{
|
|
QFormLayout *form = NULL;
|
|
QGroupBox *groupBox = NULL;
|
|
QFileInfo fileInfo(avail_shader->pass[i].source.path);
|
|
QString shaderBasename = fileInfo.completeBaseName();
|
|
QHBoxLayout *filterScaleHBoxLayout = NULL;
|
|
QComboBox *filterComboBox = new QComboBox(this);
|
|
QComboBox *scaleComboBox = new QComboBox(this);
|
|
QToolButton *moveDownButton = NULL;
|
|
QToolButton *moveUpButton = NULL;
|
|
unsigned j = 0;
|
|
|
|
/* Sometimes video_shader shows 1 pass with no source file, when there are really 0 passes. */
|
|
if (shaderBasename.isEmpty())
|
|
continue;
|
|
|
|
hasPasses = true;
|
|
|
|
filterComboBox->setProperty("pass", i);
|
|
scaleComboBox->setProperty("pass", i);
|
|
|
|
moveDownButton = new QToolButton(this);
|
|
moveDownButton->setText("↓");
|
|
moveDownButton->setProperty("pass", i);
|
|
|
|
moveUpButton = new QToolButton(this);
|
|
moveUpButton->setText("↑");
|
|
moveUpButton->setProperty("pass", i);
|
|
|
|
/* Can't move down if we're already at the bottom. */
|
|
if (i < avail_shader->passes - 1)
|
|
connect(moveDownButton, SIGNAL(clicked()),
|
|
this, SLOT(onShaderPassMoveDownClicked()));
|
|
else
|
|
moveDownButton->setDisabled(true);
|
|
|
|
/* Can't move up if we're already at the top. */
|
|
if (i > 0)
|
|
connect(moveUpButton, SIGNAL(clicked()),
|
|
this, SLOT(onShaderPassMoveUpClicked()));
|
|
else
|
|
moveUpButton->setDisabled(true);
|
|
|
|
for (;;)
|
|
{
|
|
QString filterLabel = getFilterLabel(j);
|
|
|
|
if (filterLabel.isEmpty())
|
|
break;
|
|
|
|
if (j == 0)
|
|
filterLabel = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
|
|
|
|
filterComboBox->addItem(filterLabel, j);
|
|
|
|
j++;
|
|
}
|
|
|
|
for (j = 0; j < 7; j++)
|
|
{
|
|
QString label;
|
|
|
|
if (j == 0)
|
|
label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
|
|
else
|
|
label = QString::number(j) + "x";
|
|
|
|
scaleComboBox->addItem(label, j);
|
|
}
|
|
|
|
filterComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].filter));
|
|
scaleComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].fbo.scale_x));
|
|
|
|
/* connect the signals only after the initial index is set */
|
|
connect(filterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onFilterComboBoxIndexChanged(int)));
|
|
connect(scaleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onScaleComboBoxIndexChanged(int)));
|
|
|
|
form = new QFormLayout();
|
|
groupBox = new QGroupBox(shaderBasename);
|
|
groupBox->setLayout(form);
|
|
groupBox->setProperty("pass", i);
|
|
groupBox->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(groupBox, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onGroupBoxContextMenuRequested(const QPoint&)));
|
|
|
|
m_layout->addWidget(groupBox);
|
|
|
|
filterScaleHBoxLayout = new QHBoxLayout();
|
|
filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
|
|
filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILTER)) + ":", this));
|
|
filterScaleHBoxLayout->addWidget(filterComboBox);
|
|
filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCALE)) + ":", this));
|
|
filterScaleHBoxLayout->addWidget(scaleComboBox);
|
|
filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(20, 0, QSizePolicy::Preferred, QSizePolicy::Preferred));
|
|
|
|
if (moveUpButton)
|
|
filterScaleHBoxLayout->addWidget(moveUpButton);
|
|
|
|
if (moveDownButton)
|
|
filterScaleHBoxLayout->addWidget(moveDownButton);
|
|
|
|
form->addRow("", filterScaleHBoxLayout);
|
|
|
|
for (j = 0; j < avail_shader->num_parameters; j++)
|
|
{
|
|
struct video_shader_parameter *param = &avail_shader->parameters[j];
|
|
|
|
if (param->pass != static_cast<int>(i))
|
|
continue;
|
|
|
|
addShaderParam(param, form);
|
|
}
|
|
}
|
|
|
|
if (!hasPasses)
|
|
{
|
|
QLabel *noParamsLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_NO_PASSES), this);
|
|
noParamsLabel->setAlignment(Qt::AlignCenter);
|
|
|
|
m_layout->addWidget(noParamsLabel);
|
|
}
|
|
|
|
m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
|
|
|
/* Why is this required?? The layout is corrupt without both resizes. */
|
|
resize(width() + 1, height());
|
|
show();
|
|
resize(width() - 1, height());
|
|
}
|
|
|
|
void ShaderParamsDialog::onParameterLabelContextMenuRequested(const QPoint&)
|
|
{
|
|
QVariant paramVariant;
|
|
QString parameter;
|
|
QPointer<QAction> action;
|
|
QList<QAction*> actions;
|
|
QScopedPointer<QAction> resetParamAction;
|
|
QLabel *label = qobject_cast<QLabel*>(sender());
|
|
|
|
if (!label)
|
|
return;
|
|
|
|
paramVariant = label->property("parameter");
|
|
|
|
if (!paramVariant.isValid())
|
|
return;
|
|
|
|
parameter = paramVariant.toString();
|
|
|
|
resetParamAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_PARAMETER), 0));
|
|
|
|
actions.append(resetParamAction.data());
|
|
|
|
action = QMenu::exec(actions, QCursor::pos(), NULL, label);
|
|
|
|
if (!action)
|
|
return;
|
|
|
|
if (action == resetParamAction.data())
|
|
onShaderResetParameter(parameter);
|
|
}
|
|
|
|
void ShaderParamsDialog::onGroupBoxContextMenuRequested(const QPoint&)
|
|
{
|
|
QPointer<QAction> action;
|
|
QList<QAction*> actions;
|
|
QScopedPointer<QAction> resetPassAction;
|
|
QScopedPointer<QAction> resetAllPassesAction;
|
|
QVariant passVariant;
|
|
int pass = 0;
|
|
bool ok = false;
|
|
QGroupBox *groupBox = qobject_cast<QGroupBox*>(sender());
|
|
|
|
if (!groupBox)
|
|
return;
|
|
|
|
passVariant = groupBox->property("pass");
|
|
|
|
if (!passVariant.isValid())
|
|
return;
|
|
|
|
pass = passVariant.toInt(&ok);
|
|
|
|
if (!ok)
|
|
return;
|
|
|
|
resetPassAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_PASS), 0));
|
|
resetAllPassesAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_ALL_PASSES), 0));
|
|
|
|
actions.append(resetPassAction.data());
|
|
actions.append(resetAllPassesAction.data());
|
|
|
|
action = QMenu::exec(actions, QCursor::pos(), NULL, groupBox);
|
|
|
|
if (!action)
|
|
return;
|
|
|
|
if (action == resetPassAction.data())
|
|
onShaderResetPass(pass);
|
|
else if (action == resetAllPassesAction.data())
|
|
onShaderResetAllPasses();
|
|
}
|
|
|
|
void ShaderParamsDialog::addShaderParam(struct video_shader_parameter *param, QFormLayout *form)
|
|
{
|
|
QString desc = param->desc;
|
|
QString parameter = param->id;
|
|
QLabel *label = new QLabel(desc);
|
|
|
|
label->setProperty("parameter", parameter);
|
|
label->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(label, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onParameterLabelContextMenuRequested(const QPoint&)));
|
|
|
|
if ((param->minimum == 0.0)
|
|
&& (param->maximum
|
|
== (param->minimum
|
|
+ param->step)))
|
|
{
|
|
/* option is basically a bool, so use a checkbox */
|
|
QCheckBox *checkBox = new QCheckBox(this);
|
|
checkBox->setChecked(param->current == param->maximum ? true : false);
|
|
checkBox->setProperty("param", parameter);
|
|
|
|
connect(checkBox, SIGNAL(clicked()), this, SLOT(onShaderParamCheckBoxClicked()));
|
|
|
|
form->addRow(label, checkBox);
|
|
}
|
|
else
|
|
{
|
|
QDoubleSpinBox *doubleSpinBox = NULL;
|
|
QSpinBox *spinBox = NULL;
|
|
QHBoxLayout *box = new QHBoxLayout();
|
|
QSlider *slider = new QSlider(Qt::Horizontal, this);
|
|
double value = lerp(
|
|
param->minimum, param->maximum, 0, 100, param->current);
|
|
double intpart = 0;
|
|
bool stepIsFractional = modf(param->step, &intpart);
|
|
|
|
slider->setRange(0, 100);
|
|
slider->setSingleStep(1);
|
|
slider->setValue(value);
|
|
slider->setProperty("param", parameter);
|
|
|
|
connect(slider, SIGNAL(valueChanged(int)),
|
|
this, SLOT(onShaderParamSliderValueChanged(int)));
|
|
|
|
box->addWidget(slider);
|
|
|
|
if (stepIsFractional)
|
|
{
|
|
doubleSpinBox = new QDoubleSpinBox(this);
|
|
doubleSpinBox->setRange(param->minimum, param->maximum);
|
|
doubleSpinBox->setSingleStep(param->step);
|
|
doubleSpinBox->setValue(param->current);
|
|
doubleSpinBox->setProperty("slider", QVariant::fromValue(slider));
|
|
slider->setProperty("doubleSpinBox", QVariant::fromValue(doubleSpinBox));
|
|
|
|
connect(doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onShaderParamDoubleSpinBoxValueChanged(double)));
|
|
|
|
box->addWidget(doubleSpinBox);
|
|
}
|
|
else
|
|
{
|
|
spinBox = new QSpinBox(this);
|
|
spinBox->setRange(param->minimum, param->maximum);
|
|
spinBox->setSingleStep(param->step);
|
|
spinBox->setValue(param->current);
|
|
spinBox->setProperty("slider", QVariant::fromValue(slider));
|
|
slider->setProperty("spinBox", QVariant::fromValue(spinBox));
|
|
|
|
connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSpinBoxValueChanged(int)));
|
|
|
|
box->addWidget(spinBox);
|
|
}
|
|
|
|
form->addRow(label, box);
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderParamCheckBoxClicked()
|
|
{
|
|
QVariant paramVariant;
|
|
QCheckBox *checkBox = qobject_cast<QCheckBox*>(sender());
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!checkBox)
|
|
return;
|
|
|
|
if (menu_shader && menu_shader->passes == 0)
|
|
return;
|
|
|
|
paramVariant = checkBox->property("param");
|
|
|
|
if (paramVariant.isValid())
|
|
{
|
|
QString parameter = paramVariant.toString();
|
|
|
|
if (menu_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
QString id = menu_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &menu_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
QString id = video_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &video_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderParamSliderValueChanged(int)
|
|
{
|
|
QVariant spinBoxVariant;
|
|
QVariant paramVariant;
|
|
QSlider *slider = qobject_cast<QSlider*>(sender());
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
double newValue = 0.0;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!slider)
|
|
return;
|
|
|
|
spinBoxVariant = slider->property("spinBox");
|
|
paramVariant = slider->property("param");
|
|
|
|
if (paramVariant.isValid())
|
|
{
|
|
QString parameter = paramVariant.toString();
|
|
|
|
if (menu_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
QString id = menu_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &menu_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
newValue = lerp(0, 100, param->minimum, param->maximum, slider->value());
|
|
newValue = round(newValue / param->step) * param->step;
|
|
param->current = newValue;
|
|
}
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
QString id = video_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &video_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
newValue = lerp(0, 100, param->minimum, param->maximum, slider->value());
|
|
newValue = round(newValue / param->step) * param->step;
|
|
param->current = newValue;
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
|
|
}
|
|
|
|
if (spinBoxVariant.isValid())
|
|
{
|
|
QSpinBox *spinBox = spinBoxVariant.value<QSpinBox*>();
|
|
|
|
if (!spinBox)
|
|
return;
|
|
|
|
spinBox->blockSignals(true);
|
|
spinBox->setValue(newValue);
|
|
spinBox->blockSignals(false);
|
|
}
|
|
else
|
|
{
|
|
QVariant doubleSpinBoxVariant = slider->property("doubleSpinBox");
|
|
QDoubleSpinBox *doubleSpinBox = doubleSpinBoxVariant.value<QDoubleSpinBox*>();
|
|
|
|
if (!doubleSpinBox)
|
|
return;
|
|
|
|
doubleSpinBox->blockSignals(true);
|
|
doubleSpinBox->setValue(newValue);
|
|
doubleSpinBox->blockSignals(false);
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderParamSpinBoxValueChanged(int value)
|
|
{
|
|
QVariant sliderVariant;
|
|
QVariant paramVariant;
|
|
QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender());
|
|
QSlider *slider = NULL;
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!spinBox)
|
|
return;
|
|
|
|
sliderVariant = spinBox->property("slider");
|
|
|
|
if (!sliderVariant.isValid())
|
|
return;
|
|
|
|
slider = sliderVariant.value<QSlider*>();
|
|
|
|
if (!slider)
|
|
return;
|
|
|
|
paramVariant = slider->property("param");
|
|
|
|
if (paramVariant.isValid())
|
|
{
|
|
QString parameter = paramVariant.toString();
|
|
double newValue = 0.0;
|
|
|
|
if (menu_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
QString id = menu_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &menu_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
param->current = value;
|
|
newValue = lerp(
|
|
param->minimum, param->maximum, 0, 100, param->current);
|
|
slider->blockSignals(true);
|
|
slider->setValue(newValue);
|
|
slider->blockSignals(false);
|
|
}
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
QString id = video_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &video_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
param->current = value;
|
|
newValue = lerp(
|
|
param->minimum, param->maximum, 0, 100, param->current);
|
|
slider->blockSignals(true);
|
|
slider->setValue(newValue);
|
|
slider->blockSignals(false);
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderParamsDialog::onShaderParamDoubleSpinBoxValueChanged(double value)
|
|
{
|
|
QVariant sliderVariant;
|
|
QVariant paramVariant;
|
|
QSlider *slider = NULL;
|
|
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(sender());
|
|
struct video_shader *menu_shader = NULL;
|
|
struct video_shader *video_shader = NULL;
|
|
|
|
getShaders(&menu_shader, &video_shader);
|
|
|
|
if (!doubleSpinBox)
|
|
return;
|
|
|
|
sliderVariant = doubleSpinBox->property("slider");
|
|
|
|
if (!sliderVariant.isValid())
|
|
return;
|
|
|
|
slider = sliderVariant.value<QSlider*>();
|
|
|
|
if (!slider)
|
|
return;
|
|
|
|
paramVariant = slider->property("param");
|
|
|
|
if (paramVariant.isValid())
|
|
{
|
|
QString parameter = paramVariant.toString();
|
|
double newValue = 0.0;
|
|
|
|
if (menu_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
|
|
{
|
|
QString id = menu_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &menu_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
param->current = value;
|
|
newValue = lerp(
|
|
param->minimum, param->maximum, 0, 100, param->current);
|
|
slider->blockSignals(true);
|
|
slider->setValue(newValue);
|
|
slider->blockSignals(false);
|
|
}
|
|
}
|
|
|
|
if (video_shader)
|
|
{
|
|
int i;
|
|
struct video_shader_parameter *param = NULL;
|
|
|
|
for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
|
|
{
|
|
QString id = video_shader->parameters[i].id;
|
|
|
|
if (id == parameter)
|
|
param = &video_shader->parameters[i];
|
|
}
|
|
|
|
if (param)
|
|
{
|
|
param->current = value;
|
|
newValue = lerp(
|
|
param->minimum, param->maximum, 0, 100, param->current);
|
|
slider->blockSignals(true);
|
|
slider->setValue(newValue);
|
|
slider->blockSignals(false);
|
|
}
|
|
|
|
video_shader->flags |= SHDR_FLAG_MODIFIED;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|