diff --git a/src/duckstation-qt/postprocessingchainconfigwidget.cpp b/src/duckstation-qt/postprocessingchainconfigwidget.cpp index 1968b9140..df93e0fdd 100644 --- a/src/duckstation-qt/postprocessingchainconfigwidget.cpp +++ b/src/duckstation-qt/postprocessingchainconfigwidget.cpp @@ -1,5 +1,6 @@ #include "postprocessingchainconfigwidget.h" #include "frontend-common/postprocessing_chain.h" +#include "postprocessingshaderconfigwidget.h" #include #include #include @@ -72,7 +73,8 @@ void PostProcessingChainConfigWidget::updateButtonStates() std::optional index = getSelectedIndex(); m_ui.remove->setEnabled(index.has_value()); m_ui.clear->setEnabled(!m_chain.IsEmpty()); - m_ui.shaderSettings->setEnabled(index.has_value()); + m_ui.shaderSettings->setEnabled(index.has_value() && (index.value() < m_chain.GetStageCount()) && + m_chain.GetShaderStage(index.value()).HasOptions()); if (index.has_value()) { @@ -106,7 +108,7 @@ void PostProcessingChainConfigWidget::onAddButtonClicked() QMessageBox::critical(this, tr("Error"), tr("Failed to add shader. The log may contain more information.")); return; } - + updateList(); configChanged(); }); @@ -168,7 +170,10 @@ void PostProcessingChainConfigWidget::onMoveDownButtonClicked() void PostProcessingChainConfigWidget::onShaderConfigButtonClicked() { std::optional index = getSelectedIndex(); - if (index.has_value()) + if (index.has_value() && index.value() < m_chain.GetStageCount()) { + PostProcessingShaderConfigWidget shader_config(this, &m_chain.GetShaderStage(index.value())); + connect(&shader_config, &PostProcessingShaderConfigWidget::configChanged, [this]() { configChanged(); }); + shader_config.exec(); } } diff --git a/src/duckstation-qt/postprocessingshaderconfigwidget.cpp b/src/duckstation-qt/postprocessingshaderconfigwidget.cpp index e69de29bb..b899bf88f 100644 --- a/src/duckstation-qt/postprocessingshaderconfigwidget.cpp +++ b/src/duckstation-qt/postprocessingshaderconfigwidget.cpp @@ -0,0 +1,151 @@ +#include "postprocessingshaderconfigwidget.h" +#include +#include +#include +#include +#include + +using FrontendCommon::PostProcessingShader; + +PostProcessingShaderConfigWidget::PostProcessingShaderConfigWidget(QWidget* parent, + FrontendCommon::PostProcessingShader* shader) + : QDialog(parent), m_shader(shader) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("%1 Shader Options").arg(QString::fromStdString(m_shader->GetName()))); + createUi(); +} + +PostProcessingShaderConfigWidget::~PostProcessingShaderConfigWidget() = default; + +void PostProcessingShaderConfigWidget::createUi() +{ + QGridLayout* layout = new QGridLayout(this); + u32 row = 0; + + for (PostProcessingShader::Option& option : m_shader->GetOptions()) + { + if (option.type == PostProcessingShader::Option::Type::Bool) + { + QCheckBox* checkbox = new QCheckBox(QString::fromStdString(option.ui_name), this); + checkbox->setChecked(option.value[0].bool_value); + connect(checkbox, &QCheckBox::stateChanged, [this, &option](int state) { + option.value[0].bool_value = (state == Qt::Checked); + configChanged(); + }); + connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults, [&option, checkbox]() { + QSignalBlocker sb(checkbox); + checkbox->setChecked(option.default_value[0].bool_value); + option.value = option.default_value; + }); + layout->addWidget(checkbox, row, 0, 1, 3, Qt::AlignLeft); + row++; + } + else + { + for (u32 i = 0; i < option.vector_size; i++) + { + QString label; + if (option.vector_size <= 1) + { + label = QStringLiteral("%1").arg(QString::fromStdString(option.ui_name)); + } + else + { + static constexpr std::array suffixes = { + {QT_TR_NOOP("Red"), QT_TR_NOOP("Green"), QT_TR_NOOP("Blue"), QT_TR_NOOP("Alpha")}}; + label = tr("%1 (%2)").arg(QString::fromStdString(option.ui_name)).arg(tr(suffixes[i])); + } + + layout->addWidget(new QLabel(label, this), row, 0, 1, 1, Qt::AlignLeft); + + QSlider* slider = new QSlider(Qt::Horizontal, this); + layout->addWidget(slider, row, 1, 1, 1, Qt::AlignLeft); + + QLabel* slider_label = new QLabel(this); + layout->addWidget(slider_label, row, 2, 1, 1, Qt::AlignLeft); + + if (option.type == PostProcessingShader::Option::Type::Int) + { + slider_label->setText(QStringLiteral("%1").arg(option.value[i].int_value)); + + const int range = std::max(option.max_value[i].int_value - option.min_value[i].int_value, 1); + const int step_value = + (option.step_value[i].int_value != 0) ? option.step_value[i].int_value : ((range + 99) / 100); + const int num_steps = range / step_value; + slider->setMinimum(0); + slider->setMaximum(num_steps); + slider->setSingleStep(1); + slider->setTickInterval(step_value); + slider->setValue((option.value[i].int_value - option.min_value[i].int_value) / step_value); + connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label, step_value](int value) { + const int new_value = std::clamp(option.min_value[i].int_value + (value * option.step_value[i].int_value), + option.min_value[i].int_value, option.max_value[i].int_value); + option.value[i].int_value = new_value; + slider_label->setText(QStringLiteral("%1").arg(new_value)); + configChanged(); + }); + connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults, + [&option, i, slider, slider_label, step_value]() { + QSignalBlocker sb(slider); + slider->setValue((option.default_value[i].int_value - option.min_value[i].int_value) / step_value); + slider_label->setText(QStringLiteral("%1").arg(option.default_value[i].int_value)); + option.value = option.default_value; + }); + } + else + { + slider_label->setText(QStringLiteral("%1").arg(option.value[i].float_value)); + + const float range = std::max(option.max_value[i].float_value - option.min_value[i].float_value, 1.0f); + const float step_value = + (option.step_value[i].float_value != 0) ? option.step_value[i].float_value : ((range + 99.0f) / 100.0f); + const float num_steps = range / step_value; + slider->setMinimum(0); + slider->setMaximum(num_steps); + slider->setSingleStep(1); + slider->setTickInterval(step_value); + slider->setValue( + static_cast((option.value[i].float_value - option.min_value[i].float_value) / step_value)); + connect(slider, &QSlider::valueChanged, [this, &option, i, slider_label, step_value](int value) { + const float new_value = std::clamp(option.min_value[i].float_value + + (static_cast(value) * option.step_value[i].float_value), + option.min_value[i].float_value, option.max_value[i].float_value); + option.value[i].float_value = new_value; + slider_label->setText(QStringLiteral("%1").arg(new_value)); + configChanged(); + }); + connect(this, &PostProcessingShaderConfigWidget::resettingtoDefaults, + [&option, i, slider, slider_label, step_value]() { + QSignalBlocker sb(slider); + slider->setValue(static_cast( + (option.default_value[i].float_value - option.min_value[i].float_value) / step_value)); + slider_label->setText(QStringLiteral("%1").arg(option.default_value[i].float_value)); + option.value = option.default_value; + }); + } + + row++; + } + } + } + + QPushButton* reset_button = new QPushButton(tr("Reset to Defaults"), this); + connect(reset_button, &QPushButton::clicked, this, &PostProcessingShaderConfigWidget::onResetToDefaultsClicked); + layout->addWidget(reset_button, row, 0, 1, 2); + + QPushButton* close_button = new QPushButton(tr("Close"), this); + connect(close_button, &QPushButton::clicked, this, &PostProcessingShaderConfigWidget::onCloseClicked); + layout->addWidget(close_button, row, 2, 1, 2); +} + +void PostProcessingShaderConfigWidget::onCloseClicked() +{ + done(0); +} + +void PostProcessingShaderConfigWidget::onResetToDefaultsClicked() +{ + resettingtoDefaults(); + configChanged(); +} diff --git a/src/duckstation-qt/postprocessingshaderconfigwidget.h b/src/duckstation-qt/postprocessingshaderconfigwidget.h index e69de29bb..8453cec77 100644 --- a/src/duckstation-qt/postprocessingshaderconfigwidget.h +++ b/src/duckstation-qt/postprocessingshaderconfigwidget.h @@ -0,0 +1,26 @@ +#pragma once +#include "frontend-common/postprocessing_shader.h" +#include + +class PostProcessingShaderConfigWidget : public QDialog +{ + Q_OBJECT + +public: + PostProcessingShaderConfigWidget(QWidget* parent, FrontendCommon::PostProcessingShader* shader); + ~PostProcessingShaderConfigWidget(); + +Q_SIGNALS: + void configChanged(); + void resettingtoDefaults(); + +private Q_SLOTS: + void onCloseClicked(); + void onResetToDefaultsClicked(); + +private: + void createUi(); + + FrontendCommon::PostProcessingShader* m_shader; +}; +