dolphin/Source/Core/DolphinWX/PostProcessingConfigDiag.cpp
EmptyChaos ef0d21299a WX: HiDPI: VideoConfigDiag
Resolved "TODO" for Texture Cache safety, added explanation message.
Resolved "TODO" for default description, no longer uses default text for sizing

Fixed a memory leak in PostProcessingConfigDiag where it was never freeing any
of the objects it allocated in its constructor.

Minor design change to PostProcessingConfigDiag to give padding around elements
consistent with the rest of Dolphin's user interface (5px).
2016-10-04 13:47:22 +11:00

338 lines
12 KiB
C++

// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <cmath>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/notebook.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include "Common/StringUtil.h"
#include "DolphinWX/PostProcessingConfigDiag.h"
#include "VideoCommon/RenderBase.h"
PostProcessingConfigDiag::PostProcessingConfigDiag(wxWindow* parent, const std::string& shader)
: wxDialog(parent, wxID_ANY, _("Post Processing Shader Configuration")), m_shader(shader)
{
// Depending on if we are running already, either use the one from the videobackend
// or generate our own.
if (g_renderer && g_renderer->GetPostProcessor())
{
m_post_processor = g_renderer->GetPostProcessor()->GetConfig();
}
else
{
m_post_processor = new PostProcessingShaderConfiguration();
m_post_processor->LoadShader(m_shader);
}
// Create our UI classes
const PostProcessingShaderConfiguration::ConfigMap& config_map = m_post_processor->GetOptions();
std::vector<std::unique_ptr<ConfigGrouping>> config_groups;
config_groups.reserve(config_map.size());
m_config_map.reserve(config_map.size());
for (const auto& it : config_map)
{
std::unique_ptr<ConfigGrouping> group;
if (it.second.m_type ==
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL)
{
group = std::make_unique<ConfigGrouping>(ConfigGrouping::WidgetType::TYPE_TOGGLE,
it.second.m_gui_name, it.first,
it.second.m_dependent_option, &it.second);
}
else
{
group = std::make_unique<ConfigGrouping>(ConfigGrouping::WidgetType::TYPE_SLIDER,
it.second.m_gui_name, it.first,
it.second.m_dependent_option, &it.second);
}
m_config_map[it.first] = group.get();
config_groups.emplace_back(std::move(group));
}
// Arrange our vectors based on dependency
for (auto& group : config_groups)
{
const std::string& parent_name = group->GetParent();
if (parent_name.empty())
{
// It doesn't have a parent, just push it to the vector
m_config_groups.emplace_back(std::move(group));
}
else
{
// Since it depends on a different object, push it to a parent's object
m_config_map[parent_name]->AddChild(std::move(group));
}
}
config_groups.clear(); // Full of null unique_ptrs now
config_groups.shrink_to_fit();
const int space5 = FromDIP(5);
const int space10 = FromDIP(10);
// Generate our UI
wxNotebook* const notebook = new wxNotebook(this, wxID_ANY);
wxPanel* const page_general = new wxPanel(notebook);
wxFlexGridSizer* const szr_general = new wxFlexGridSizer(2, space5, space5);
// Now let's actually populate our window with our information
bool add_general_page = false;
for (const auto& it : m_config_groups)
{
if (it->HasChildren())
{
// Options with children get their own tab
wxPanel* const page_option = new wxPanel(notebook);
wxBoxSizer* const wrap_sizer = new wxBoxSizer(wxVERTICAL);
wxFlexGridSizer* const szr_option = new wxFlexGridSizer(2, space10, space5);
it->GenerateUI(this, page_option, szr_option);
// Add all the children
for (const auto& child : it->GetChildren())
{
child->GenerateUI(this, page_option, szr_option);
}
wrap_sizer->AddSpacer(space5);
wrap_sizer->Add(szr_option, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
wrap_sizer->AddSpacer(space5);
page_option->SetSizerAndFit(wrap_sizer);
notebook->AddPage(page_option, it->GetGUIName());
}
else
{
// Options with no children go in to the general tab
if (!add_general_page)
{
// Make it so it doesn't show up if there aren't any options without children.
add_general_page = true;
}
it->GenerateUI(this, page_general, szr_general);
}
}
if (add_general_page)
{
wxBoxSizer* const wrap_sizer = new wxBoxSizer(wxVERTICAL);
wrap_sizer->AddSpacer(space5);
wrap_sizer->Add(szr_general, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
wrap_sizer->AddSpacer(space5);
page_general->SetSizerAndFit(wrap_sizer);
notebook->InsertPage(0, page_general, _("General"));
}
// Close Button
wxStdDialogButtonSizer* const btn_strip = CreateStdDialogButtonSizer(wxOK | wxNO_DEFAULT);
btn_strip->GetAffirmativeButton()->SetLabel(_("Close"));
SetEscapeId(wxID_OK); // Treat closing the window by 'X' or hitting escape as 'OK'
wxBoxSizer* const szr_main = new wxBoxSizer(wxVERTICAL);
szr_main->AddSpacer(space5);
szr_main->Add(notebook, 1, wxEXPAND | wxLEFT | wxRIGHT, space5);
szr_main->AddSpacer(space5);
szr_main->Add(btn_strip, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
szr_main->AddSpacer(space5);
szr_main->SetMinSize(FromDIP(wxSize(400, -1)));
SetLayoutAdaptationMode(wxDIALOG_ADAPTATION_MODE_ENABLED);
SetLayoutAdaptationLevel(wxDIALOG_ADAPTATION_STANDARD_SIZER);
SetSizerAndFit(szr_main);
Center();
SetFocus();
UpdateWindowUI();
}
PostProcessingConfigDiag::~PostProcessingConfigDiag()
{
m_post_processor->SaveOptionsConfiguration();
if (g_renderer && g_renderer->GetPostProcessor())
m_post_processor = nullptr;
else
delete m_post_processor;
}
void PostProcessingConfigDiag::ConfigGrouping::GenerateUI(PostProcessingConfigDiag* dialog,
wxWindow* parent, wxFlexGridSizer* sizer)
{
if (m_type == WidgetType::TYPE_TOGGLE)
{
m_option_checkbox = new wxCheckBox(parent, wxID_ANY, m_gui_name);
m_option_checkbox->SetValue(m_config_option->m_bool_value);
m_option_checkbox->Bind(wxEVT_CHECKBOX, &PostProcessingConfigDiag::Event_CheckBox, dialog,
wxID_ANY, wxID_ANY, new UserEventData(m_option));
sizer->Add(m_option_checkbox);
sizer->AddStretchSpacer();
}
else
{
size_t vector_size = 0;
if (m_config_option->m_type ==
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER)
vector_size = m_config_option->m_integer_values.size();
else
vector_size = m_config_option->m_float_values.size();
wxFlexGridSizer* const szr_values = new wxFlexGridSizer(vector_size + 1);
wxStaticText* const option_static_text = new wxStaticText(parent, wxID_ANY, m_gui_name);
sizer->Add(option_static_text);
for (size_t i = 0; i < vector_size; ++i)
{
// wxSlider uses a signed integer for it's minimum and maximum values
// This won't work for floats.
// Let's determine how many steps we can take and use that instead.
int steps = 0;
int current_value = 0;
std::string string_value;
if (m_config_option->m_type ==
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER)
{
// Find out our range by taking the max subtracting the minimum.
double range =
m_config_option->m_integer_max_values[i] - m_config_option->m_integer_min_values[i];
// How many steps we have is the range divided by the step interval configured.
// This may not be 100% spot on accurate since developers can have odd stepping intervals
// set.
// Round up so if it is outside our range, then set it to the minimum or maximum
steps = std::ceil(range / (double)m_config_option->m_integer_step_values[i]);
// Default value is just the currently set value here
current_value = m_config_option->m_integer_values[i];
string_value = std::to_string(m_config_option->m_integer_values[i]);
}
else
{
// Same as above but with floats
float range =
m_config_option->m_float_max_values[i] - m_config_option->m_float_min_values[i];
steps = std::ceil(range / m_config_option->m_float_step_values[i]);
// We need to convert our default float value from a float to the nearest step value range
current_value =
(m_config_option->m_float_values[i] / m_config_option->m_float_step_values[i]);
string_value = std::to_string(m_config_option->m_float_values[i]);
}
DolphinSlider* slider =
new DolphinSlider(parent, wxID_ANY, current_value, 0, steps, wxDefaultPosition,
parent->FromDIP(wxSize(200, -1)), wxSL_HORIZONTAL | wxSL_BOTTOM);
wxTextCtrl* text_ctrl = new wxTextCtrl(parent, wxID_ANY, string_value);
// Disable the textctrl, it's only there to show the absolute value from the slider
text_ctrl->Disable();
// wxWidget takes over the pointer provided to it in the event handler.
// This won't be a memory leak, it'll be destroyed on dialog close.
slider->Bind(wxEVT_SLIDER, &PostProcessingConfigDiag::Event_Slider, dialog, wxID_ANY,
wxID_ANY, new UserEventData(m_option));
m_option_sliders.push_back(slider);
m_option_text_ctrls.push_back(text_ctrl);
}
if (vector_size == 1)
{
szr_values->Add(m_option_sliders[0], 0, wxALIGN_CENTER_VERTICAL);
szr_values->Add(m_option_text_ctrls[0], 0, wxALIGN_CENTER_VERTICAL);
sizer->Add(szr_values);
}
else
{
wxFlexGridSizer* const szr_inside = new wxFlexGridSizer(2);
for (size_t i = 0; i < vector_size; ++i)
{
szr_inside->Add(m_option_sliders[i], 0, wxALIGN_CENTER_VERTICAL);
szr_inside->Add(m_option_text_ctrls[i], 0, wxALIGN_CENTER_VERTICAL);
}
szr_values->Add(szr_inside);
sizer->Add(szr_values);
}
}
}
void PostProcessingConfigDiag::ConfigGrouping::EnableDependentChildren(bool enable)
{
// Enable or disable all the children
for (auto& it : m_children)
{
if (it->m_type == ConfigGrouping::WidgetType::TYPE_TOGGLE)
{
it->m_option_checkbox->Enable(enable);
}
else
{
for (auto& slider : it->m_option_sliders)
slider->Enable(enable);
}
// Set this objects children as well
it->EnableDependentChildren(enable);
}
}
void PostProcessingConfigDiag::Event_CheckBox(wxCommandEvent& ev)
{
UserEventData* config_option = (UserEventData*)ev.GetEventUserData();
ConfigGrouping* config = m_config_map[config_option->GetData()];
m_post_processor->SetOptionb(config->GetOption(), ev.IsChecked());
config->EnableDependentChildren(ev.IsChecked());
ev.Skip();
}
void PostProcessingConfigDiag::Event_Slider(wxCommandEvent& ev)
{
UserEventData* config_option = (UserEventData*)ev.GetEventUserData();
ConfigGrouping* config = m_config_map[config_option->GetData()];
const auto& option_data = m_post_processor->GetOption(config->GetOption());
size_t vector_size = 0;
if (option_data.m_type ==
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER)
vector_size = option_data.m_integer_values.size();
else
vector_size = option_data.m_float_values.size();
for (size_t i = 0; i < vector_size; ++i)
{
// Got to do this garbage again.
// Convert current step in to the real range value
int current_step = config->GetSliderValue(i);
std::string string_value;
if (option_data.m_type ==
PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER)
{
s32 value =
option_data.m_integer_step_values[i] * current_step + option_data.m_integer_min_values[i];
m_post_processor->SetOptioni(config->GetOption(), i, value);
string_value = std::to_string(value);
}
else
{
float value =
option_data.m_float_step_values[i] * current_step + option_data.m_float_min_values[i];
m_post_processor->SetOptionf(config->GetOption(), i, value);
string_value = std::to_string(value);
}
// Update the text box to include the new value
config->SetSliderText(i, string_value);
}
ev.Skip();
}