mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-09-05 11:57:34 +02:00
Refactored menu logic into MenuController interface, updated RT64
This commit is contained in:
parent
09bacbe82c
commit
e9e42322f0
@ -114,7 +114,8 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_events.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
|
||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __RECOMP_UI__
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
@ -9,15 +10,30 @@ namespace Rml {
|
||||
class ElementDocument;
|
||||
class EventListenerInstancer;
|
||||
class Context;
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace recomp {
|
||||
class UiEventListenerInstancer;
|
||||
|
||||
class MenuController {
|
||||
public:
|
||||
virtual ~MenuController() {}
|
||||
virtual Rml::ElementDocument* load_document(Rml::Context* context) = 0;
|
||||
virtual void register_events(UiEventListenerInstancer& listener) = 0;
|
||||
virtual void make_bindings(Rml::Context* context) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<MenuController> create_launcher_menu();
|
||||
std::unique_ptr<MenuController> create_config_menu();
|
||||
|
||||
using event_handler_t = void(Rml::Event&);
|
||||
|
||||
void queue_event(const SDL_Event& event);
|
||||
bool try_deque_event(SDL_Event& out);
|
||||
|
||||
std::unique_ptr<Rml::EventListenerInstancer> make_event_listener_instancer();
|
||||
void make_ui_bindings(Rml::Context* context);
|
||||
std::unique_ptr<UiEventListenerInstancer> make_event_listener_instancer();
|
||||
void register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler);
|
||||
|
||||
enum class Menu {
|
||||
Launcher,
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit f57eeec44a49cf4fb3ffe6c22d9f0c3d5410fb72
|
||||
Subproject commit a9a74b5c1975e081bc0a23f29545a0954f5a2f90
|
102
src/ui/ui_config.cpp
Normal file
102
src/ui/ui_config.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "recomp_ui.h"
|
||||
#include "../../ultramodern/config.hpp"
|
||||
#include "RmlUi/Core.h"
|
||||
|
||||
ultramodern::GraphicsConfig cur_options;
|
||||
ultramodern::GraphicsConfig new_options;
|
||||
Rml::DataModelHandle options_handle;
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, {
|
||||
{ultramodern::Resolution::Original, "Original"},
|
||||
{ultramodern::Resolution::Original2x, "Original2x"},
|
||||
{ultramodern::Resolution::Auto, "Auto"},
|
||||
});
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::WindowMode, {
|
||||
{ultramodern::WindowMode::Windowed, "Windowed"},
|
||||
{ultramodern::WindowMode::Fullscreen, "Fullscreen"}
|
||||
});
|
||||
|
||||
template <typename T>
|
||||
void get_option(const T& input, Rml::Variant& output) {
|
||||
std::string value = "";
|
||||
to_json(value, input);
|
||||
|
||||
if (value.empty()) {
|
||||
throw std::runtime_error("Invalid value :" + std::to_string(int(input)));
|
||||
}
|
||||
|
||||
output = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_option(T& output, const Rml::Variant& input) {
|
||||
T value = T::OptionCount;
|
||||
from_json(input.Get<std::string>(), value);
|
||||
|
||||
if (value == T::OptionCount) {
|
||||
throw std::runtime_error("Invalid value :" + input.Get<std::string>());
|
||||
}
|
||||
|
||||
output = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) {
|
||||
constructor.BindFunc(name,
|
||||
[option](Rml::Variant& out) { get_option(*option, out); },
|
||||
[option](const Rml::Variant& in) { set_option(*option, in); options_handle.DirtyVariable("options_changed"); }
|
||||
);
|
||||
};
|
||||
|
||||
class ConfigMenu : public recomp::MenuController {
|
||||
public:
|
||||
ConfigMenu() {
|
||||
|
||||
}
|
||||
~ConfigMenu() override {
|
||||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
return context->LoadDocument("assets/config_menu.rml");
|
||||
}
|
||||
void register_events(recomp::UiEventListenerInstancer& listener) override {
|
||||
recomp::register_event(listener, "apply_options",
|
||||
[](Rml::Event& event) {
|
||||
cur_options = new_options;
|
||||
options_handle.DirtyVariable("options_changed");
|
||||
update_graphics_config(new_options);
|
||||
});
|
||||
}
|
||||
void make_bindings(Rml::Context* context) override {
|
||||
Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model");
|
||||
if (!constructor) {
|
||||
throw std::runtime_error("Failed to make RmlUi data model for the config menu");
|
||||
}
|
||||
|
||||
bind_option(constructor, "res_option", &new_options.res_option);
|
||||
bind_option(constructor, "wm_option", &new_options.wm_option);
|
||||
bind_option(constructor, "ar_option", &new_options.ar_option);
|
||||
bind_option(constructor, "msaa_option", &new_options.msaa_option);
|
||||
bind_option(constructor, "rr_option", &new_options.rr_option);
|
||||
constructor.BindFunc("rr_manual_value",
|
||||
[](Rml::Variant& out) {
|
||||
out = new_options.rr_manual_value;
|
||||
},
|
||||
[](const Rml::Variant& in) {
|
||||
new_options.rr_manual_value = in.Get<int>();
|
||||
options_handle.DirtyVariable("options_changed");
|
||||
});
|
||||
|
||||
constructor.BindFunc("options_changed",
|
||||
[](Rml::Variant& out) {
|
||||
out = (cur_options != new_options);
|
||||
});
|
||||
|
||||
options_handle = constructor.GetModelHandle();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<recomp::MenuController> recomp::create_config_menu() {
|
||||
return std::make_unique<ConfigMenu>();
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include "recomp_ui.h"
|
||||
#include "../../ultramodern/ultramodern.hpp"
|
||||
#include "../../ultramodern/config.hpp"
|
||||
#include "common/rt64_user_configuration.h"
|
||||
|
||||
#include "nfd.h"
|
||||
#include "RmlUi/Core.h"
|
||||
|
||||
using event_handler_t = void(Rml::Event&);
|
||||
|
||||
class UiEventListener : public Rml::EventListener {
|
||||
event_handler_t* handler_;
|
||||
public:
|
||||
UiEventListener(event_handler_t* handler) : handler_(handler) {}
|
||||
void ProcessEvent(Rml::Event& event) override {
|
||||
handler_(event);
|
||||
}
|
||||
};
|
||||
|
||||
class UiEventListenerInstancer : public Rml::EventListenerInstancer {
|
||||
std::unordered_map<Rml::String, UiEventListener> listener_map_;
|
||||
public:
|
||||
Rml::EventListener* InstanceEventListener(const Rml::String& value, Rml::Element* element) override {
|
||||
printf("Instancing event listener for %s\n", value.c_str());
|
||||
auto find_it = listener_map_.find(value);
|
||||
|
||||
if (find_it != listener_map_.end()) {
|
||||
return &find_it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void register_event(const Rml::String& value, event_handler_t* handler) {
|
||||
listener_map_.emplace(value, UiEventListener{ handler });
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, {
|
||||
{ultramodern::Resolution::Original, "Original"},
|
||||
{ultramodern::Resolution::Original2x, "Original2x"},
|
||||
{ultramodern::Resolution::Auto, "Auto"},
|
||||
});
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::WindowMode, {
|
||||
{ultramodern::WindowMode::Windowed, "Windowed"},
|
||||
{ultramodern::WindowMode::Fullscreen, "Fullscreen"}
|
||||
});
|
||||
|
||||
ultramodern::GraphicsConfig cur_options;
|
||||
ultramodern::GraphicsConfig new_options;
|
||||
Rml::DataModelHandle options_handle;
|
||||
|
||||
template <typename T>
|
||||
void get_option(const T& input, Rml::Variant& output) {
|
||||
std::string value = "";
|
||||
to_json(value, input);
|
||||
|
||||
if (value.empty()) {
|
||||
throw std::runtime_error("Invalid value :" + std::to_string(int(input)));
|
||||
}
|
||||
|
||||
output = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_option(T& output, const Rml::Variant& input) {
|
||||
T value = T::OptionCount;
|
||||
from_json(input.Get<std::string>(), value);
|
||||
|
||||
if (value == T::OptionCount) {
|
||||
throw std::runtime_error("Invalid value :" + input.Get<std::string>());
|
||||
}
|
||||
|
||||
output = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) {
|
||||
constructor.BindFunc(name,
|
||||
[option](Rml::Variant& out) { get_option(*option, out); },
|
||||
[option](const Rml::Variant& in) { set_option(*option, in); options_handle.DirtyVariable("options_changed"); }
|
||||
);
|
||||
};
|
||||
|
||||
void recomp::make_ui_bindings(Rml::Context* context) {
|
||||
Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model");
|
||||
if (!constructor) {
|
||||
throw std::runtime_error("Failed to make RmlUi data model constructor");
|
||||
}
|
||||
|
||||
bind_option(constructor, "res_option", &new_options.res_option);
|
||||
bind_option(constructor, "wm_option", &new_options.wm_option);
|
||||
bind_option(constructor, "ar_option", &new_options.ar_option);
|
||||
bind_option(constructor, "msaa_option", &new_options.msaa_option);
|
||||
bind_option(constructor, "rr_option", &new_options.rr_option);
|
||||
constructor.BindFunc("rr_manual_value",
|
||||
[](Rml::Variant& out) {
|
||||
out = new_options.rr_manual_value;
|
||||
},
|
||||
[](const Rml::Variant& in) {
|
||||
new_options.rr_manual_value = in.Get<int>();
|
||||
options_handle.DirtyVariable("options_changed");
|
||||
});
|
||||
|
||||
constructor.BindFunc("options_changed",
|
||||
[](Rml::Variant& out) {
|
||||
out = (cur_options != new_options);
|
||||
});
|
||||
|
||||
options_handle = constructor.GetModelHandle();
|
||||
}
|
||||
|
||||
std::unique_ptr<Rml::EventListenerInstancer> recomp::make_event_listener_instancer() {
|
||||
std::unique_ptr<UiEventListenerInstancer> ret = std::make_unique<UiEventListenerInstancer>();
|
||||
|
||||
ret->register_event("start_game",
|
||||
[](Rml::Event& event) {
|
||||
ultramodern::start_game(0);
|
||||
set_current_menu(Menu::Config);
|
||||
}
|
||||
);
|
||||
|
||||
ret->register_event("apply_options",
|
||||
[](Rml::Event& event) {
|
||||
cur_options = new_options;
|
||||
options_handle.DirtyVariable("options_changed");
|
||||
update_graphics_config(new_options);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
31
src/ui/ui_launcher.cpp
Normal file
31
src/ui/ui_launcher.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "recomp_ui.h"
|
||||
#include "../../ultramodern/ultramodern.hpp"
|
||||
#include "RmlUi/Core.h"
|
||||
|
||||
class LauncherMenu : public recomp::MenuController {
|
||||
public:
|
||||
LauncherMenu() {
|
||||
|
||||
}
|
||||
~LauncherMenu() override {
|
||||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
return context->LoadDocument("assets/launcher.rml");
|
||||
}
|
||||
void register_events(recomp::UiEventListenerInstancer& listener) override {
|
||||
recomp::register_event(listener, "start_game",
|
||||
[](Rml::Event& event) {
|
||||
ultramodern::start_game(0);
|
||||
recomp::set_current_menu(recomp::Menu::Config);
|
||||
}
|
||||
);
|
||||
}
|
||||
void make_bindings(Rml::Context* context) override {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<recomp::MenuController> recomp::create_launcher_menu() {
|
||||
return std::make_unique<LauncherMenu>();
|
||||
}
|
@ -586,9 +586,44 @@ Rml::Element* get_target(Rml::ElementDocument* document, Rml::Element* element)
|
||||
return element;
|
||||
}
|
||||
|
||||
namespace recomp {
|
||||
class UiEventListener : public Rml::EventListener {
|
||||
event_handler_t* handler_;
|
||||
public:
|
||||
UiEventListener(event_handler_t* handler) : handler_(handler) {}
|
||||
void ProcessEvent(Rml::Event& event) override {
|
||||
handler_(event);
|
||||
}
|
||||
};
|
||||
|
||||
class UiEventListenerInstancer : public Rml::EventListenerInstancer {
|
||||
std::unordered_map<Rml::String, UiEventListener> listener_map_;
|
||||
public:
|
||||
Rml::EventListener* InstanceEventListener(const Rml::String& value, Rml::Element* element) override {
|
||||
printf("Instancing event listener for %s\n", value.c_str());
|
||||
auto find_it = listener_map_.find(value);
|
||||
|
||||
if (find_it != listener_map_.end()) {
|
||||
return &find_it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void register_event(const Rml::String& value, event_handler_t* handler) {
|
||||
listener_map_.emplace(value, UiEventListener{ handler });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void recomp::register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler) {
|
||||
listener.register_event(name, handler);
|
||||
}
|
||||
|
||||
struct {
|
||||
struct UIRenderContext render;
|
||||
class {
|
||||
std::unordered_map<recomp::Menu, std::unique_ptr<recomp::MenuController>> menus;
|
||||
std::unordered_map<recomp::Menu, Rml::ElementDocument*> documents;
|
||||
Rml::ElementDocument* current_document;
|
||||
Rml::Element* prev_focused;
|
||||
@ -596,7 +631,7 @@ struct {
|
||||
SystemInterface_SDL system_interface;
|
||||
std::unique_ptr<RmlRenderInterface_RT64> render_interface;
|
||||
Rml::Context* context;
|
||||
std::unique_ptr<Rml::EventListenerInstancer> event_listener_instancer;
|
||||
recomp::UiEventListenerInstancer event_listener_instancer;
|
||||
|
||||
void unload() {
|
||||
render_interface.reset();
|
||||
@ -636,11 +671,24 @@ struct {
|
||||
current_document = nullptr;
|
||||
|
||||
documents.clear();
|
||||
Rml::Factory::RegisterEventListenerInstancer(event_listener_instancer.get());
|
||||
Rml::Factory::RegisterEventListenerInstancer(&event_listener_instancer);
|
||||
}
|
||||
|
||||
documents.emplace(recomp::Menu::Launcher, context->LoadDocument("assets/launcher.rml"));
|
||||
documents.emplace(recomp::Menu::Config, context->LoadDocument("assets/config_menu.rml"));
|
||||
for (auto& [menu, controller]: menus) {
|
||||
documents.emplace(menu, controller->load_document(context));
|
||||
}
|
||||
}
|
||||
|
||||
void make_event_listeners() {
|
||||
for (auto& [menu, controller]: menus) {
|
||||
controller->register_events(event_listener_instancer);
|
||||
}
|
||||
}
|
||||
|
||||
void make_bindings() {
|
||||
for (auto& [menu, controller]: menus) {
|
||||
controller->make_bindings(context);
|
||||
}
|
||||
}
|
||||
|
||||
void update_focus(bool mouse_moved) {
|
||||
@ -679,6 +727,10 @@ struct {
|
||||
prev_focused = current_document->GetFocusLeafNode();
|
||||
}
|
||||
}
|
||||
|
||||
void add_menu(recomp::Menu menu, std::unique_ptr<recomp::MenuController>&& controller) {
|
||||
menus.emplace(menu, std::move(controller));
|
||||
}
|
||||
} rml;
|
||||
} UIContext;
|
||||
|
||||
@ -688,17 +740,20 @@ extern SDL_Window* window;
|
||||
void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
printf("RT64 hook init\n");
|
||||
|
||||
UIContext.rml.add_menu(recomp::Menu::Config, recomp::create_config_menu());
|
||||
UIContext.rml.add_menu(recomp::Menu::Launcher, recomp::create_launcher_menu());
|
||||
|
||||
UIContext.render.interface = interface;
|
||||
UIContext.render.device = device;
|
||||
|
||||
// Setup RML
|
||||
UIContext.rml.system_interface.SetWindow(window);
|
||||
UIContext.rml.render_interface = std::make_unique<RmlRenderInterface_RT64>(&UIContext.render);
|
||||
UIContext.rml.event_listener_instancer = recomp::make_event_listener_instancer();
|
||||
UIContext.rml.make_event_listeners();
|
||||
|
||||
Rml::SetSystemInterface(&UIContext.rml.system_interface);
|
||||
Rml::SetRenderInterface(UIContext.rml.render_interface.get());
|
||||
Rml::Factory::RegisterEventListenerInstancer(UIContext.rml.event_listener_instancer.get());
|
||||
Rml::Factory::RegisterEventListenerInstancer(&UIContext.rml.event_listener_instancer);
|
||||
|
||||
Rml::Initialise();
|
||||
|
||||
@ -706,7 +761,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||
|
||||
UIContext.rml.context = Rml::CreateContext("main", Rml::Vector2i(width, height));
|
||||
recomp::make_ui_bindings(UIContext.rml.context);
|
||||
UIContext.rml.make_bindings();
|
||||
|
||||
Rml::Debugger::Initialise(UIContext.rml.context);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user