1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 18:35:20 +00:00
OpenMW/apps/openmw/mwgui/postprocessorhud.cpp

484 lines
18 KiB
C++

#include "postprocessorhud.hpp"
#include <MyGUI_Align.h>
#include <MyGUI_Button.h>
#include <MyGUI_Delegate.h>
#include <MyGUI_EditBox.h>
#include <MyGUI_EventPair.h>
#include <MyGUI_FactoryManager.h>
#include <MyGUI_Gui.h>
#include <MyGUI_InputManager.h>
#include <MyGUI_Macros.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_TabItem.h>
#include <MyGUI_UString.h>
#include <MyGUI_Widget.h>
#include <MyGUI_WidgetDefines.h>
#include <MyGUI_WidgetInput.h>
#include <MyGUI_Window.h>
#include <components/fx/technique.hpp>
#include <components/fx/widgets.hpp>
#include <components/misc/utf8stream.hpp>
#include <components/widgets/box.hpp>
#include "../mwrender/postprocessor.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
namespace MWGui
{
void PostProcessorHud::ListWrapper::onKeyButtonPressed(MyGUI::KeyCode key, MyGUI::Char ch)
{
if (MyGUI::InputManager::getInstance().isShiftPressed()
&& (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
return;
MyGUI::ListBox::onKeyButtonPressed(key, ch);
}
PostProcessorHud::PostProcessorHud()
: WindowBase("openmw_postprocessor_hud.layout")
{
getWidget(mActiveList, "ActiveList");
getWidget(mInactiveList, "InactiveList");
getWidget(mConfigLayout, "ConfigLayout");
getWidget(mFilter, "Filter");
getWidget(mButtonActivate, "ButtonActivate");
getWidget(mButtonDeactivate, "ButtonDeactivate");
getWidget(mButtonUp, "ButtonUp");
getWidget(mButtonDown, "ButtonDown");
mButtonActivate->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyActivatePressed);
mButtonDeactivate->eventMouseButtonClick
+= MyGUI::newDelegate(this, &PostProcessorHud::notifyDeactivatePressed);
mButtonUp->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderUpPressed);
mButtonDown->eventMouseButtonClick += MyGUI::newDelegate(this, &PostProcessorHud::notifyShaderDownPressed);
mActiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
mInactiveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &PostProcessorHud::notifyKeyButtonPressed);
mActiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
mInactiveList->eventListChangePosition += MyGUI::newDelegate(this, &PostProcessorHud::notifyListChangePosition);
mFilter->eventEditTextChange += MyGUI::newDelegate(this, &PostProcessorHud::notifyFilterChanged);
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord
+= MyGUI::newDelegate(this, &PostProcessorHud::notifyWindowResize);
mShaderInfo = mConfigLayout->createWidget<Gui::AutoSizedEditBox>("HeaderText", {}, MyGUI::Align::Default);
mShaderInfo->setUserString("VStretch", "true");
mShaderInfo->setUserString("HStretch", "true");
mShaderInfo->setTextAlign(MyGUI::Align::Left | MyGUI::Align::Top);
mShaderInfo->setEditReadOnly(true);
mShaderInfo->setEditWordWrap(true);
mShaderInfo->setEditMultiLine(true);
mShaderInfo->setNeedMouseFocus(false);
mConfigLayout->setVisibleVScroll(true);
mConfigArea = mConfigLayout->createWidget<MyGUI::Widget>("", {}, MyGUI::Align::Default);
mConfigLayout->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
mConfigArea->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
}
void PostProcessorHud::notifyFilterChanged(MyGUI::EditBox* sender)
{
updateTechniques();
}
void PostProcessorHud::notifyWindowResize(MyGUI::Window* sender)
{
layout();
}
void PostProcessorHud::notifyResetButtonClicked(MyGUI::Widget* sender)
{
for (size_t i = 1; i < mConfigArea->getChildCount(); ++i)
{
if (auto* child = dynamic_cast<fx::Widgets::UniformBase*>(mConfigArea->getChildAt(i)))
child->toDefault();
}
}
void PostProcessorHud::notifyListChangePosition(MyGUI::ListBox* sender, size_t index)
{
if (sender == mActiveList)
mInactiveList->clearIndexSelected();
else if (sender == mInactiveList)
mActiveList->clearIndexSelected();
if (index >= sender->getItemCount())
return;
updateConfigView(sender->getItemNameAt(index));
}
void PostProcessorHud::toggleTechnique(bool enabled)
{
auto* list = enabled ? mInactiveList : mActiveList;
size_t selected = list->getIndexSelected();
if (selected != MyGUI::ITEM_NONE)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
mOverrideHint = list->getItemNameAt(selected);
auto technique = *list->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
if (technique->getDynamic())
return;
if (enabled)
processor->enableTechnique(technique);
else
processor->disableTechnique(technique);
processor->saveChain();
}
}
void PostProcessorHud::notifyActivatePressed(MyGUI::Widget* sender)
{
toggleTechnique(true);
}
void PostProcessorHud::notifyDeactivatePressed(MyGUI::Widget* sender)
{
toggleTechnique(false);
}
void PostProcessorHud::moveShader(Direction direction)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
size_t selected = mActiveList->getIndexSelected();
if (selected == MyGUI::ITEM_NONE)
return;
int index = direction == Direction::Up ? static_cast<int>(selected) - 1 : selected + 1;
index = std::clamp<int>(index, 0, mActiveList->getItemCount() - 1);
if (static_cast<size_t>(index) != selected)
{
auto technique = *mActiveList->getItemDataAt<std::shared_ptr<fx::Technique>>(selected);
if (technique->getDynamic())
return;
if (processor->enableTechnique(technique, index) != MWRender::PostProcessor::Status_Error)
processor->saveChain();
}
}
void PostProcessorHud::notifyShaderUpPressed(MyGUI::Widget* sender)
{
moveShader(Direction::Up);
}
void PostProcessorHud::notifyShaderDownPressed(MyGUI::Widget* sender)
{
moveShader(Direction::Down);
}
void PostProcessorHud::notifyKeyButtonPressed(MyGUI::Widget* sender, MyGUI::KeyCode key, MyGUI::Char ch)
{
MyGUI::ListBox* list = static_cast<MyGUI::ListBox*>(sender);
if (list->getIndexSelected() == MyGUI::ITEM_NONE)
return;
if (key == MyGUI::KeyCode::ArrowLeft && list == mActiveList)
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
toggleTechnique(false);
}
else
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mInactiveList);
mActiveList->clearIndexSelected();
select(mInactiveList, 0);
}
}
else if (key == MyGUI::KeyCode::ArrowRight && list == mInactiveList)
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
toggleTechnique(true);
}
else
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mActiveList);
mInactiveList->clearIndexSelected();
select(mActiveList, 0);
}
}
else if (list == mActiveList && MyGUI::InputManager::getInstance().isShiftPressed()
&& (key == MyGUI::KeyCode::ArrowUp || key == MyGUI::KeyCode::ArrowDown))
{
moveShader(key == MyGUI::KeyCode::ArrowUp ? Direction::Up : Direction::Down);
}
}
void PostProcessorHud::onOpen()
{
toggleMode(Settings::ShaderManager::Mode::Debug);
updateTechniques();
}
void PostProcessorHud::onClose()
{
toggleMode(Settings::ShaderManager::Mode::Normal);
}
void PostProcessorHud::layout()
{
constexpr int padding = 12;
constexpr int padding2 = padding * 2;
mShaderInfo->setCoord(
padding, padding, mConfigLayout->getSize().width - padding2 - padding, mShaderInfo->getTextSize().height);
int totalHeight = mShaderInfo->getTop() + mShaderInfo->getTextSize().height + padding;
mConfigArea->setCoord({ padding, totalHeight, mShaderInfo->getSize().width, mConfigLayout->getHeight() });
int childHeights = 0;
MyGUI::EnumeratorWidgetPtr enumerator = mConfigArea->getEnumerator();
while (enumerator.next())
{
enumerator.current()->setCoord(padding, childHeights + padding, mShaderInfo->getSize().width - padding2,
enumerator.current()->getHeight());
childHeights += enumerator.current()->getHeight() + padding;
}
totalHeight += childHeights;
mConfigArea->setSize(mConfigArea->getWidth(), childHeights);
mConfigLayout->setCanvasSize(mConfigLayout->getWidth() - padding2, totalHeight);
mConfigLayout->setSize(mConfigLayout->getWidth(), mConfigLayout->getParentSize().height - padding2);
}
void PostProcessorHud::notifyMouseWheel(MyGUI::Widget* sender, int rel)
{
int offset = mConfigLayout->getViewOffset().top + rel * 0.3;
if (offset > 0)
mConfigLayout->setViewOffset(MyGUI::IntPoint(0, 0));
else
mConfigLayout->setViewOffset(MyGUI::IntPoint(0, static_cast<int>(offset)));
}
void PostProcessorHud::select(ListWrapper* list, size_t index)
{
list->setIndexSelected(index);
notifyListChangePosition(list, index);
}
void PostProcessorHud::toggleMode(Settings::ShaderManager::Mode mode)
{
Settings::ShaderManager::get().setMode(mode);
MWBase::Environment::get().getWorld()->getPostProcessor()->toggleMode();
if (!isVisible())
return;
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
updateConfigView(mInactiveList->getItemNameAt(mInactiveList->getIndexSelected()));
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
updateConfigView(mActiveList->getItemNameAt(mActiveList->getIndexSelected()));
}
void PostProcessorHud::updateConfigView(const std::string& name)
{
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
auto technique = processor->loadTechnique(name);
if (!technique || technique->getStatus() == fx::Technique::Status::File_Not_exists)
return;
while (mConfigArea->getChildCount() > 0)
MyGUI::Gui::getInstance().destroyWidget(mConfigArea->getChildAt(0));
mShaderInfo->setCaption({});
std::ostringstream ss;
const std::string_view NA = "#{Interface:NotAvailableShort}";
const char endl = '\n';
std::string_view author = technique->getAuthor().empty() ? NA : technique->getAuthor();
std::string_view version = technique->getVersion().empty() ? NA : technique->getVersion();
std::string_view description = technique->getDescription().empty() ? NA : technique->getDescription();
auto serializeBool = [](bool value) { return value ? "#{sYes}" : "#{sNo}"; };
const auto flags = technique->getFlags();
const auto flag_interior = serializeBool(!(flags & fx::Technique::Flag_Disable_Interiors));
const auto flag_exterior = serializeBool(!(flags & fx::Technique::Flag_Disable_Exteriors));
const auto flag_underwater = serializeBool(!(flags & fx::Technique::Flag_Disable_Underwater));
const auto flag_abovewater = serializeBool(!(flags & fx::Technique::Flag_Disable_Abovewater));
switch (technique->getStatus())
{
case fx::Technique::Status::Success:
case fx::Technique::Status::Uncompiled:
{
if (technique->getDynamic())
ss << "#{fontcolourhtml=header}#{OMWShaders:ShaderLocked}: #{fontcolourhtml=normal} "
"#{OMWShaders:ShaderLockedDescription}"
<< endl
<< endl;
ss << "#{fontcolourhtml=header}#{OMWShaders:Author}: #{fontcolourhtml=normal} " << author << endl
<< endl
<< "#{fontcolourhtml=header}#{OMWShaders:Version}: #{fontcolourhtml=normal} " << version << endl
<< endl
<< "#{fontcolourhtml=header}#{OMWShaders:Description}: #{fontcolourhtml=normal} " << description
<< endl
<< endl
<< "#{fontcolourhtml=header}#{OMWShaders:InInteriors}: #{fontcolourhtml=normal} " << flag_interior
<< "#{fontcolourhtml=header} #{OMWShaders:InExteriors}: #{fontcolourhtml=normal} " << flag_exterior
<< "#{fontcolourhtml=header} #{OMWShaders:Underwater}: #{fontcolourhtml=normal} "
<< flag_underwater
<< "#{fontcolourhtml=header} #{OMWShaders:Abovewater}: #{fontcolourhtml=normal} "
<< flag_abovewater;
break;
}
case fx::Technique::Status::Parse_Error:
ss << "#{fontcolourhtml=negative}Shader Compile Error: #{fontcolourhtml=normal} <"
<< std::string(technique->getName()) << "> failed to compile." << endl
<< endl
<< technique->getLastError();
break;
case fx::Technique::Status::File_Not_exists:
break;
}
mShaderInfo->setCaptionWithReplacing(ss.str());
if (Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug)
{
if (technique->getUniformMap().size() > 0)
{
MyGUI::Button* resetButton
= mConfigArea->createWidget<MyGUI::Button>("MW_Button", { 0, 0, 0, 24 }, MyGUI::Align::Default);
resetButton->setCaptionWithReplacing("#{OMWShaders:ResetShader}");
resetButton->setTextAlign(MyGUI::Align::Center);
resetButton->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
resetButton->eventMouseButtonClick
+= MyGUI::newDelegate(this, &PostProcessorHud::notifyResetButtonClicked);
}
for (const auto& uniform : technique->getUniformMap())
{
if (!uniform->mStatic || uniform->mSamplerType)
continue;
if (!uniform->mHeader.empty())
{
Gui::AutoSizedTextBox* divider = mConfigArea->createWidget<Gui::AutoSizedTextBox>(
"MW_UniformGroup", { 0, 0, 0, 34 }, MyGUI::Align::Default);
divider->setNeedMouseFocus(false);
divider->setCaptionWithReplacing(uniform->mHeader);
}
fx::Widgets::UniformBase* uwidget = mConfigArea->createWidget<fx::Widgets::UniformBase>(
"MW_UniformEdit", { 0, 0, 0, 22 }, MyGUI::Align::Default);
uwidget->init(uniform);
uwidget->getLabel()->eventMouseWheel += MyGUI::newDelegate(this, &PostProcessorHud::notifyMouseWheel);
}
}
layout();
}
void PostProcessorHud::updateTechniques()
{
if (!isVisible())
return;
std::string hint;
ListWrapper* hintWidget = nullptr;
if (mInactiveList->getIndexSelected() != MyGUI::ITEM_NONE)
{
hint = mInactiveList->getItemNameAt(mInactiveList->getIndexSelected());
hintWidget = mInactiveList;
}
else if (mActiveList->getIndexSelected() != MyGUI::ITEM_NONE)
{
hint = mActiveList->getItemNameAt(mActiveList->getIndexSelected());
hintWidget = mActiveList;
}
mInactiveList->removeAllItems();
mActiveList->removeAllItems();
auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor();
for (const auto& [name, _] : processor->getTechniqueMap())
{
auto technique = processor->loadTechnique(name);
if (!technique)
continue;
if (!technique->getHidden() && !processor->isTechniqueEnabled(technique))
{
std::string lowerName = Utf8Stream::lowerCaseUtf8(name);
std::string lowerCaption = mFilter->getCaption();
lowerCaption = Utf8Stream::lowerCaseUtf8(lowerCaption);
if (lowerName.find(lowerCaption) != std::string::npos)
mInactiveList->addItem(name, technique);
}
}
for (auto technique : processor->getTechniques())
{
if (!technique->getHidden())
mActiveList->addItem(technique->getName(), technique);
}
auto tryFocus = [this](ListWrapper* widget, const std::string& hint) {
MyGUI::Widget* oldFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (oldFocus == mFilter)
return;
size_t index = widget->findItemIndexWith(hint);
if (index != MyGUI::ITEM_NONE)
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(widget);
select(widget, index);
}
};
if (!mOverrideHint.empty())
{
tryFocus(mActiveList, mOverrideHint);
tryFocus(mInactiveList, mOverrideHint);
mOverrideHint.clear();
}
else if (hintWidget && !hint.empty())
tryFocus(hintWidget, hint);
}
void PostProcessorHud::registerMyGUIComponents()
{
MyGUI::FactoryManager& factory = MyGUI::FactoryManager::getInstance();
factory.registerFactory<fx::Widgets::UniformBase>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat4>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat3>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat2>("Widget");
factory.registerFactory<fx::Widgets::EditNumberFloat>("Widget");
factory.registerFactory<fx::Widgets::EditNumberInt>("Widget");
factory.registerFactory<fx::Widgets::EditBool>("Widget");
factory.registerFactory<ListWrapper>("Widget");
}
}