mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
758 lines
20 KiB
C++
758 lines
20 KiB
C++
//
|
|
//
|
|
|
|
// Undefine stupid windows API defines that get included by the prefix headers
|
|
#ifdef GetNextSibling
|
|
#undef GetNextSibling
|
|
#endif
|
|
|
|
#ifdef GetFirstChild
|
|
#undef GetFirstChild
|
|
#endif
|
|
|
|
#include "scpui/rocket_ui.h"
|
|
|
|
#include "cfile/cfile.h"
|
|
#include "io/mouse.h"
|
|
#include "mod_table/mod_table.h"
|
|
#include "osapi/osapi.h"
|
|
#include "scpui/IncludeNodeHandler.h"
|
|
#include "scpui/RocketDecoratorsInstancer.h"
|
|
#include "scpui/RocketFileInterface.h"
|
|
#include "scpui/RocketLuaSystemInterface.h"
|
|
#include "scpui/RocketRenderingInterface.h"
|
|
#include "scpui/RocketSystemInterface.h"
|
|
#include "scpui/SoundPlugin.h"
|
|
#include "scpui/elements/AnimationElement.h"
|
|
#include "scpui/elements/ScrollingTextElement.h"
|
|
#include "scripting/scripting.h"
|
|
|
|
// Our Assert conflicts with the definitions inside libRocket
|
|
#pragma push_macro("Assert")
|
|
#undef Assert
|
|
|
|
#include <Rocket/Controls.h>
|
|
#include <Rocket/Core.h>
|
|
|
|
#include <Rocket/Debugger.h>
|
|
|
|
#include <Rocket/Controls/Lua/Controls.h>
|
|
#include <Rocket/Core/Lua/Interpreter.h>
|
|
|
|
#pragma pop_macro("Assert")
|
|
|
|
#include <codecvt>
|
|
#include <locale>
|
|
|
|
|
|
using namespace Rocket::Core;
|
|
|
|
namespace {
|
|
bool rocket_initialized = false;
|
|
|
|
std::unique_ptr<scpui::RocketRenderingInterface> rendering_interface;
|
|
std::unique_ptr<scpui::RocketSystemInterface> system_interface;
|
|
std::unique_ptr<scpui::RocketFileInterface> file_interface;
|
|
|
|
Context* input_context = nullptr;
|
|
vec2d render_offset = {0.f, 0.f};
|
|
|
|
bool debugger_initialized = false;
|
|
|
|
/**
|
|
* @brief Retrieves the current cursor position in render-target pixels
|
|
*
|
|
* @details SDL reports event coordinates in window pixels, but a libRocket context is sized from
|
|
* gr_screen.max_w/max_h and renders through gr_set_2d_matrix, which is also render space. Rather
|
|
* than duplicating the conversion here we let mouse_get_real_pos do it.
|
|
*
|
|
* Note that this is the cursor position as of right now rather than the one carried by the event,
|
|
* so if several motion events are queued in a single os_poll libRocket sees the latest position
|
|
* for all of them. That is what it wants for hover state anyway.
|
|
*/
|
|
vec2d get_render_space_mouse_pos()
|
|
{
|
|
int x = 0;
|
|
int y = 0;
|
|
mouse_get_real_pos(&x, &y);
|
|
|
|
return {i2fl(x), i2fl(y)};
|
|
}
|
|
|
|
bool transform_to_rocket(vec2d& in)
|
|
{
|
|
in = {in.x - render_offset.x, in.y - render_offset.y};
|
|
|
|
if (in.x < 0.f || in.y < 0.f) {
|
|
// Out of bounds
|
|
return false;
|
|
}
|
|
|
|
auto dims = input_context->GetDimensions();
|
|
if (in.x > dims.x || in.y > dims.y) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int get_modifier_state()
|
|
{
|
|
int mods = 0;
|
|
|
|
auto sdl_mods = SDL_GetModState();
|
|
|
|
if (sdl_mods & SDL_KMOD_CTRL) {
|
|
mods |= Input::KM_CTRL;
|
|
}
|
|
|
|
if (sdl_mods & SDL_KMOD_SHIFT) {
|
|
mods |= Input::KM_SHIFT;
|
|
}
|
|
|
|
if (sdl_mods & SDL_KMOD_ALT) {
|
|
mods |= Input::KM_ALT;
|
|
}
|
|
|
|
if (sdl_mods & SDL_KMOD_GUI) {
|
|
mods |= Input::KM_META;
|
|
}
|
|
|
|
if (sdl_mods & SDL_KMOD_CAPS) {
|
|
mods |= Input::KM_CAPSLOCK;
|
|
}
|
|
|
|
if (sdl_mods & SDL_KMOD_NUM) {
|
|
mods |= Input::KM_NUMLOCK;
|
|
}
|
|
|
|
auto states = SDL_GetKeyboardState(nullptr);
|
|
if (states[SDL_SCANCODE_SCROLLLOCK]) {
|
|
mods |= Input::KM_SCROLLLOCK;
|
|
}
|
|
|
|
return mods;
|
|
}
|
|
|
|
void load_fonts()
|
|
{
|
|
SCP_vector<SCP_string> files;
|
|
cf_get_file_list(files, CF_TYPE_FONT, "*.ttf");
|
|
|
|
for (auto& name : files) {
|
|
// The file extension gets removed by CFile
|
|
FontDatabase::LoadFontFace((name + ".ttf").c_str());
|
|
}
|
|
|
|
files.clear();
|
|
cf_get_file_list(files, CF_TYPE_FONT, "*.otf");
|
|
|
|
for (auto& name : files) {
|
|
// The file extension gets removed by CFile
|
|
FontDatabase::LoadFontFace((name + ".otf").c_str());
|
|
}
|
|
}
|
|
|
|
bool mouse_motion_handler(const SDL_Event& evt)
|
|
{
|
|
if (!os::events::isWindowEvent(evt, os::getSDLMainWindow())) {
|
|
// Ignore events that are not relevant to the main window
|
|
return false;
|
|
}
|
|
|
|
if (input_context == nullptr) {
|
|
// No active input context
|
|
return false;
|
|
}
|
|
|
|
vec2d evt_pos = get_render_space_mouse_pos();
|
|
if (!transform_to_rocket(evt_pos)) {
|
|
// Out of bounds
|
|
return false;
|
|
}
|
|
|
|
input_context->ProcessMouseMove((int)evt_pos.x, (int)evt_pos.y, get_modifier_state());
|
|
|
|
// Don't consume motion: the engine's own handler still has to track the cursor position, which
|
|
// mouse_get_pos() and thereby e.g. the cursor drawn into the VR headset image depend on.
|
|
return false;
|
|
}
|
|
|
|
bool mouse_button_handler(const SDL_Event& evt)
|
|
{
|
|
if (!os::events::isWindowEvent(evt, os::getSDLMainWindow())) {
|
|
// Ignore events that are not relevant to the main window
|
|
return false;
|
|
}
|
|
|
|
if (input_context == nullptr) {
|
|
// No active input context
|
|
return false;
|
|
}
|
|
|
|
vec2d evt_pos = get_render_space_mouse_pos();
|
|
if (!transform_to_rocket(evt_pos)) {
|
|
// Out of bounds
|
|
return false;
|
|
}
|
|
|
|
int button;
|
|
switch (evt.button.button) {
|
|
case SDL_BUTTON_LEFT:
|
|
button = 0;
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
button = 2;
|
|
break;
|
|
case SDL_BUTTON_RIGHT:
|
|
button = 1;
|
|
break;
|
|
case SDL_BUTTON_X1:
|
|
button = 3;
|
|
break;
|
|
case SDL_BUTTON_X2:
|
|
button = 4;
|
|
break;
|
|
default:
|
|
// Unknown SDL button value
|
|
mprintf(("Unknown SDL button value %d in libRocket mouse handler!\n", evt.button.button));
|
|
return false;
|
|
}
|
|
|
|
if (evt.type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
|
// Up
|
|
input_context->ProcessMouseButtonUp(button, get_modifier_state());
|
|
} else {
|
|
// Down
|
|
input_context->ProcessMouseButtonDown(button, get_modifier_state());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mouse_wheel_handler(const SDL_Event& evt)
|
|
{
|
|
if (!os::events::isWindowEvent(evt, os::getSDLMainWindow())) {
|
|
// Ignore events that are not relevant to the main window
|
|
return false;
|
|
}
|
|
|
|
if (input_context == nullptr) {
|
|
// No active input context
|
|
return false;
|
|
}
|
|
|
|
int wheel_value = fl2i(evt.wheel.y);
|
|
|
|
if (evt.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
|
|
wheel_value *= -1;
|
|
}
|
|
|
|
// libRocket expects a different direction than what SDL delivers
|
|
input_context->ProcessMouseWheel(-wheel_value, get_modifier_state());
|
|
|
|
return true;
|
|
}
|
|
|
|
Input::KeyIdentifier translateKey(SDL_Keycode Key)
|
|
{
|
|
switch (Key) {
|
|
case SDLK_A:
|
|
return Rocket::Core::Input::KI_A;
|
|
case SDLK_B:
|
|
return Rocket::Core::Input::KI_B;
|
|
case SDLK_C:
|
|
return Rocket::Core::Input::KI_C;
|
|
case SDLK_D:
|
|
return Rocket::Core::Input::KI_D;
|
|
case SDLK_E:
|
|
return Rocket::Core::Input::KI_E;
|
|
case SDLK_F:
|
|
return Rocket::Core::Input::KI_F;
|
|
case SDLK_G:
|
|
return Rocket::Core::Input::KI_G;
|
|
case SDLK_H:
|
|
return Rocket::Core::Input::KI_H;
|
|
case SDLK_I:
|
|
return Rocket::Core::Input::KI_I;
|
|
case SDLK_J:
|
|
return Rocket::Core::Input::KI_J;
|
|
case SDLK_K:
|
|
return Rocket::Core::Input::KI_K;
|
|
case SDLK_L:
|
|
return Rocket::Core::Input::KI_L;
|
|
case SDLK_M:
|
|
return Rocket::Core::Input::KI_M;
|
|
case SDLK_N:
|
|
return Rocket::Core::Input::KI_N;
|
|
case SDLK_O:
|
|
return Rocket::Core::Input::KI_O;
|
|
case SDLK_P:
|
|
return Rocket::Core::Input::KI_P;
|
|
case SDLK_Q:
|
|
return Rocket::Core::Input::KI_Q;
|
|
case SDLK_R:
|
|
return Rocket::Core::Input::KI_R;
|
|
case SDLK_S:
|
|
return Rocket::Core::Input::KI_S;
|
|
case SDLK_T:
|
|
return Rocket::Core::Input::KI_T;
|
|
case SDLK_U:
|
|
return Rocket::Core::Input::KI_U;
|
|
case SDLK_V:
|
|
return Rocket::Core::Input::KI_V;
|
|
case SDLK_W:
|
|
return Rocket::Core::Input::KI_W;
|
|
case SDLK_X:
|
|
return Rocket::Core::Input::KI_X;
|
|
case SDLK_Y:
|
|
return Rocket::Core::Input::KI_Y;
|
|
case SDLK_Z:
|
|
return Rocket::Core::Input::KI_Z;
|
|
case SDLK_0:
|
|
return Rocket::Core::Input::KI_0;
|
|
case SDLK_1:
|
|
return Rocket::Core::Input::KI_1;
|
|
case SDLK_2:
|
|
return Rocket::Core::Input::KI_2;
|
|
case SDLK_3:
|
|
return Rocket::Core::Input::KI_3;
|
|
case SDLK_4:
|
|
return Rocket::Core::Input::KI_4;
|
|
case SDLK_5:
|
|
return Rocket::Core::Input::KI_5;
|
|
case SDLK_6:
|
|
return Rocket::Core::Input::KI_6;
|
|
case SDLK_7:
|
|
return Rocket::Core::Input::KI_7;
|
|
case SDLK_8:
|
|
return Rocket::Core::Input::KI_8;
|
|
case SDLK_9:
|
|
return Rocket::Core::Input::KI_9;
|
|
case SDLK_KP_0:
|
|
return Rocket::Core::Input::KI_NUMPAD0;
|
|
case SDLK_KP_1:
|
|
return Rocket::Core::Input::KI_NUMPAD1;
|
|
case SDLK_KP_2:
|
|
return Rocket::Core::Input::KI_NUMPAD2;
|
|
case SDLK_KP_3:
|
|
return Rocket::Core::Input::KI_NUMPAD3;
|
|
case SDLK_KP_4:
|
|
return Rocket::Core::Input::KI_NUMPAD4;
|
|
case SDLK_KP_5:
|
|
return Rocket::Core::Input::KI_NUMPAD5;
|
|
case SDLK_KP_6:
|
|
return Rocket::Core::Input::KI_NUMPAD6;
|
|
case SDLK_KP_7:
|
|
return Rocket::Core::Input::KI_NUMPAD7;
|
|
case SDLK_KP_8:
|
|
return Rocket::Core::Input::KI_NUMPAD8;
|
|
case SDLK_KP_9:
|
|
return Rocket::Core::Input::KI_NUMPAD9;
|
|
case SDLK_LEFT:
|
|
return Rocket::Core::Input::KI_LEFT;
|
|
case SDLK_RIGHT:
|
|
return Rocket::Core::Input::KI_RIGHT;
|
|
case SDLK_UP:
|
|
return Rocket::Core::Input::KI_UP;
|
|
case SDLK_DOWN:
|
|
return Rocket::Core::Input::KI_DOWN;
|
|
case SDLK_KP_PLUS:
|
|
return Rocket::Core::Input::KI_ADD;
|
|
case SDLK_KP_MINUS:
|
|
return Rocket::Core::Input::KI_SUBTRACT;
|
|
case SDLK_KP_MULTIPLY:
|
|
return Rocket::Core::Input::KI_MULTIPLY;
|
|
case SDLK_KP_DIVIDE:
|
|
return Rocket::Core::Input::KI_DIVIDE;
|
|
case SDLK_KP_DECIMAL:
|
|
return Rocket::Core::Input::KI_DECIMAL;
|
|
case SDLK_KP_ENTER:
|
|
return Rocket::Core::Input::KI_NUMPADENTER;
|
|
case SDLK_MINUS:
|
|
return Rocket::Core::Input::KI_OEM_MINUS;
|
|
case SDLK_EQUALS:
|
|
return Rocket::Core::Input::KI_OEM_PLUS;
|
|
case SDLK_BACKSPACE:
|
|
return Rocket::Core::Input::KI_BACK;
|
|
case SDLK_LEFTBRACKET:
|
|
return Rocket::Core::Input::KI_OEM_4;
|
|
case SDLK_RIGHTBRACKET:
|
|
return Rocket::Core::Input::KI_OEM_6;
|
|
case SDLK_BACKSLASH:
|
|
return Rocket::Core::Input::KI_OEM_5;
|
|
case SDLK_SEMICOLON:
|
|
return Rocket::Core::Input::KI_OEM_1;
|
|
case SDLK_APOSTROPHE:
|
|
return Rocket::Core::Input::KI_OEM_7;
|
|
case SDLK_COMMA:
|
|
return Rocket::Core::Input::KI_OEM_COMMA;
|
|
case SDLK_PERIOD:
|
|
return Rocket::Core::Input::KI_OEM_PERIOD;
|
|
case SDLK_SLASH:
|
|
return Rocket::Core::Input::KI_OEM_2;
|
|
case SDLK_INSERT:
|
|
return Rocket::Core::Input::KI_INSERT;
|
|
case SDLK_HOME:
|
|
return Rocket::Core::Input::KI_HOME;
|
|
case SDLK_PAGEUP:
|
|
return Rocket::Core::Input::KI_PRIOR;
|
|
case SDLK_DELETE:
|
|
return Rocket::Core::Input::KI_DELETE;
|
|
case SDLK_END:
|
|
return Rocket::Core::Input::KI_END;
|
|
case SDLK_PAGEDOWN:
|
|
return Rocket::Core::Input::KI_NEXT;
|
|
case SDLK_F1:
|
|
return Rocket::Core::Input::KI_F1;
|
|
case SDLK_F2:
|
|
return Rocket::Core::Input::KI_F2;
|
|
case SDLK_F3:
|
|
return Rocket::Core::Input::KI_F3;
|
|
case SDLK_F4:
|
|
return Rocket::Core::Input::KI_F4;
|
|
case SDLK_F5:
|
|
return Rocket::Core::Input::KI_F5;
|
|
case SDLK_F6:
|
|
return Rocket::Core::Input::KI_F6;
|
|
case SDLK_F7:
|
|
return Rocket::Core::Input::KI_F7;
|
|
case SDLK_F8:
|
|
return Rocket::Core::Input::KI_F8;
|
|
case SDLK_F9:
|
|
return Rocket::Core::Input::KI_F9;
|
|
case SDLK_F10:
|
|
return Rocket::Core::Input::KI_F10;
|
|
case SDLK_F11:
|
|
return Rocket::Core::Input::KI_F11;
|
|
case SDLK_F12:
|
|
return Rocket::Core::Input::KI_F12;
|
|
case SDLK_F13:
|
|
return Rocket::Core::Input::KI_F13;
|
|
case SDLK_F14:
|
|
return Rocket::Core::Input::KI_F14;
|
|
case SDLK_F15:
|
|
return Rocket::Core::Input::KI_F15;
|
|
case SDLK_ESCAPE:
|
|
return Rocket::Core::Input::KI_ESCAPE;
|
|
case SDLK_LCTRL:
|
|
return Rocket::Core::Input::KI_LCONTROL;
|
|
case SDLK_RCTRL:
|
|
return Rocket::Core::Input::KI_RCONTROL;
|
|
case SDLK_LALT:
|
|
return Rocket::Core::Input::KI_LMETA;
|
|
case SDLK_RALT:
|
|
return Rocket::Core::Input::KI_RMETA;
|
|
case SDLK_CAPSLOCK:
|
|
return Rocket::Core::Input::KI_CAPITAL;
|
|
case SDLK_PAUSE:
|
|
return Rocket::Core::Input::KI_PAUSE;
|
|
case SDLK_RETURN:
|
|
return Rocket::Core::Input::KI_RETURN;
|
|
case SDLK_LSHIFT:
|
|
return Rocket::Core::Input::KI_LSHIFT;
|
|
case SDLK_RSHIFT:
|
|
return Rocket::Core::Input::KI_RSHIFT;
|
|
case SDLK_SPACE:
|
|
return Rocket::Core::Input::KI_SPACE;
|
|
case SDLK_TAB:
|
|
return Rocket::Core::Input::KI_TAB;
|
|
};
|
|
|
|
return Rocket::Core::Input::KI_UNKNOWN;
|
|
}
|
|
|
|
bool key_event_handler(const SDL_Event& evt)
|
|
{
|
|
if (!os::events::isWindowEvent(evt, os::getSDLMainWindow())) {
|
|
// Ignore events that are not relevant to the main window
|
|
return false;
|
|
}
|
|
|
|
if (input_context == nullptr) {
|
|
// No active input context
|
|
return false;
|
|
}
|
|
|
|
// Check for special key combinations
|
|
auto sdl_mods = SDL_GetModState();
|
|
|
|
auto shift_down = (sdl_mods & SDL_KMOD_SHIFT) != 0;
|
|
|
|
if (evt.key.key == SDLK_ESCAPE && shift_down) {
|
|
// Ignore this combination to still enable quitting
|
|
return false;
|
|
}
|
|
if (evt.key.key == SDLK_PRINTSCREEN) {
|
|
// Ignore print screen buttons to still allow the default engine actions
|
|
return false;
|
|
}
|
|
if (evt.key.key == SDLK_LSHIFT || evt.key.key == SDLK_RSHIFT || evt.key.key == SDLK_LCTRL ||
|
|
evt.key.key == SDLK_RCTRL||evt.key.key == SDLK_LALT||evt.key.key == SDLK_RALT) {
|
|
// Ignore simple key events of modifiers since they are not meaningful to libRocket
|
|
return false;
|
|
}
|
|
|
|
if (evt.type == SDL_EVENT_KEY_UP) {
|
|
auto ctrl_down = (sdl_mods & SDL_KMOD_CTRL) != 0;
|
|
|
|
if (evt.key.key == SDLK_R && ctrl_down && shift_down) {
|
|
// Ctrl+Shift+R reloads everything
|
|
scpui::reloadAllContexts();
|
|
|
|
return true;
|
|
} else if (evt.key.key == SDLK_D && ctrl_down && shift_down) {
|
|
// Ctrl+Shift+D toggles the debugger
|
|
if (!debugger_initialized) {
|
|
// First we need to initialize the debugger. We use the current context for that, should work in
|
|
// most cases
|
|
if (!Rocket::Debugger::Initialise(input_context)) {
|
|
Warning(LOCATION, "Failed to initialize debugger!");
|
|
return true;
|
|
}
|
|
|
|
debugger_initialized = true;
|
|
}
|
|
|
|
Rocket::Debugger::SetContext(input_context);
|
|
|
|
// Toggle the debugger state
|
|
Rocket::Debugger::SetVisible(!Rocket::Debugger::IsVisible());
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (evt.type == SDL_EVENT_KEY_DOWN) {
|
|
return input_context->ProcessKeyDown(translateKey(evt.key.key), get_modifier_state());
|
|
} else {
|
|
return input_context->ProcessKeyUp(translateKey(evt.key.key), get_modifier_state());
|
|
}
|
|
}
|
|
|
|
bool text_input_handler(const SDL_Event& evt)
|
|
{
|
|
if (!os::events::isWindowEvent(evt, os::getSDLMainWindow())) {
|
|
// Ignore events that are not relevant to the main window
|
|
return false;
|
|
}
|
|
|
|
if (input_context == nullptr) {
|
|
// No active input context
|
|
return false;
|
|
}
|
|
|
|
// libRocket expects UCS-2 so we first need to convert to that
|
|
// NOTE: not using SDL_iconv_utf8_ucs2() macro here due to Linux compatiblity issue
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
const char *tocode = "UCS-2BE";
|
|
#else
|
|
const char *tocode = "UCS-2LE";
|
|
#endif
|
|
|
|
auto text = reinterpret_cast<char16_t *>(SDL_iconv_string(tocode, "UTF-8", evt.text.text, SDL_strlen(evt.text.text)+1));
|
|
|
|
if ( !text ) {
|
|
// encoding failed
|
|
return false;
|
|
}
|
|
|
|
auto ucs2String = std::u16string(text);
|
|
|
|
SDL_free(text);
|
|
|
|
bool consumed = true;
|
|
for (auto& c : ucs2String) {
|
|
consumed = consumed && input_context->ProcessTextInput(c);
|
|
}
|
|
|
|
return consumed;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace scpui {
|
|
|
|
void initialize()
|
|
{
|
|
if (!Unicode_text_mode) {
|
|
mprintf(("NOTE: libRocket is disabled since unicode text mode is not enabled!\n"));
|
|
return;
|
|
}
|
|
|
|
rendering_interface.reset(new RocketRenderingInterface());
|
|
system_interface.reset(new RocketSystemInterface());
|
|
file_interface.reset(new RocketFileInterface());
|
|
|
|
rendering_interface->setRenderOffset(render_offset);
|
|
|
|
// Setup our custom interfaces
|
|
Rocket::Core::SetRenderInterface(rendering_interface.get());
|
|
Rocket::Core::SetSystemInterface(system_interface.get());
|
|
Rocket::Core::SetFileInterface(file_interface.get());
|
|
|
|
Rocket::Core::Initialise();
|
|
// Initialise the Rocket Controls library.
|
|
Rocket::Controls::Initialise();
|
|
|
|
Rocket::Core::Factory::RegisterElementInstancer("ani", new ElementInstancerGeneric<elements::AnimationElement>())
|
|
->RemoveReference();
|
|
Rocket::Core::Factory::RegisterElementInstancer("scrollingText",
|
|
new ElementInstancerGeneric<elements::ScrollingTextElement>())
|
|
->RemoveReference();
|
|
|
|
XMLParser::RegisterNodeHandler("include", new IncludeNodeHandler())->RemoveReference();
|
|
|
|
// Register custom underline decorator with its own instancer
|
|
Rocket::Core::DecoratorInstancer* underline_instancer = new scpui::decorators::UnderlineDecoratorInstancer();
|
|
Rocket::Core::Factory::RegisterDecoratorInstancer("underline", underline_instancer);
|
|
|
|
// Register custom corner-borders decorator with its own instancer
|
|
Rocket::Core::DecoratorInstancer* corner_borders_instancer = new scpui::decorators::BorderDecoratorInstancer();
|
|
Rocket::Core::Factory::RegisterDecoratorInstancer("corner-borders", corner_borders_instancer);
|
|
|
|
// Decrease the reference counts (as it is managed by libRocket after registration)
|
|
underline_instancer->RemoveReference();
|
|
corner_borders_instancer->RemoveReference();
|
|
|
|
// Setup the plugin a style sheet properties for the sound support
|
|
Rocket::Core::RegisterPlugin(new SoundPlugin());
|
|
Rocket::Core::StyleSheetSpecification::RegisterParser("sound", new SoundPropertyParser());
|
|
for (auto& type : SoundPlugin::getEventTypes()) {
|
|
auto prop_name = type + "-sound";
|
|
Rocket::Core::StyleSheetSpecification::RegisterProperty(prop_name.c_str(), "-1", false).AddParser("sound");
|
|
}
|
|
|
|
// Initialise the Lua interface
|
|
Rocket::Core::Lua::Interpreter::Initialise(Script_system.GetLuaSession(),
|
|
std::unique_ptr<Rocket::Core::Lua::LuaSystemInterface>(new RocketLuaSystemInterface()));
|
|
Rocket::Controls::Lua::RegisterTypes(Rocket::Core::Lua::Interpreter::GetLuaState());
|
|
|
|
load_fonts();
|
|
|
|
// initialize events
|
|
os::events::addEventListener(SDL_EVENT_MOUSE_MOTION, os::events::DEFAULT_LISTENER_WEIGHT - 10, mouse_motion_handler);
|
|
os::events::addEventListener(SDL_EVENT_MOUSE_BUTTON_UP, os::events::DEFAULT_LISTENER_WEIGHT - 10, mouse_button_handler);
|
|
os::events::addEventListener(SDL_EVENT_MOUSE_BUTTON_DOWN, os::events::DEFAULT_LISTENER_WEIGHT - 10, mouse_button_handler);
|
|
os::events::addEventListener(SDL_EVENT_MOUSE_WHEEL, os::events::DEFAULT_LISTENER_WEIGHT - 10, mouse_wheel_handler);
|
|
|
|
os::events::addEventListener(SDL_EVENT_KEY_UP, os::events::DEFAULT_LISTENER_WEIGHT - 10, key_event_handler);
|
|
os::events::addEventListener(SDL_EVENT_KEY_DOWN, os::events::DEFAULT_LISTENER_WEIGHT - 10, key_event_handler);
|
|
|
|
os::events::addEventListener(SDL_EVENT_TEXT_INPUT, os::events::DEFAULT_LISTENER_WEIGHT - 10, text_input_handler);
|
|
|
|
rocket_initialized = true;
|
|
}
|
|
void setOffset(float x, float y) { rendering_interface->setRenderOffset({x, y}); }
|
|
|
|
void shutdown_scripting()
|
|
{
|
|
if (!rocket_initialized) {
|
|
return;
|
|
}
|
|
Rocket::Core::Lua::Interpreter::Shutdown(false);
|
|
}
|
|
|
|
void shutdown()
|
|
{
|
|
if (!rocket_initialized) {
|
|
return;
|
|
}
|
|
|
|
if (input_context != nullptr) {
|
|
// If this is still here then the user forgot to disable input
|
|
mprintf(("Warning: The input context is still active! Disabling input...\n"));
|
|
disableInput();
|
|
}
|
|
|
|
// Shutdown Rocket.
|
|
Rocket::Core::Shutdown();
|
|
|
|
rendering_interface.reset();
|
|
system_interface.reset();
|
|
file_interface.reset();
|
|
|
|
rocket_initialized = false;
|
|
}
|
|
|
|
void reloadContext(Rocket::Core::Context* context)
|
|
{
|
|
struct DocumentStatus {
|
|
ElementDocument* original = nullptr;
|
|
String file_name;
|
|
bool visible = true;
|
|
bool focus = false;
|
|
};
|
|
|
|
// Save the current status of the documents in this context
|
|
SCP_vector<DocumentStatus> documents;
|
|
for (auto i = 0; i < context->GetNumDocuments(); ++i) {
|
|
ElementDocument* doc = context->GetDocument(i);
|
|
if (doc->GetSourceURL() == "") {
|
|
// In-memory documents can't be reloaded
|
|
continue;
|
|
}
|
|
|
|
DocumentStatus status;
|
|
status.original = doc;
|
|
status.file_name = doc->GetSourceURL();
|
|
status.visible = doc->IsVisible();
|
|
status.focus = doc->GetFocusLeafNode() != nullptr;
|
|
|
|
documents.push_back(std::move(status));
|
|
}
|
|
|
|
Factory::ClearStyleSheetCache();
|
|
Factory::ClearTemplateCache();
|
|
|
|
// And readd them all
|
|
for (auto& status : documents) {
|
|
// Selectively unload the old documents.
|
|
// We can't just unload all since that destroys the debugger
|
|
context->UnloadDocument(status.original);
|
|
|
|
auto doc = context->LoadDocument(status.file_name);
|
|
if (status.visible) {
|
|
doc->Show(status.focus ? ElementDocument::FOCUS : ElementDocument::NONE);
|
|
}
|
|
// We don't own this reference
|
|
doc->RemoveReference();
|
|
}
|
|
}
|
|
|
|
void reloadAllContexts()
|
|
{
|
|
for (auto i = 0; i < Rocket::Core::GetNumContexts(); ++i) {
|
|
reloadContext(Rocket::Core::GetContext(i));
|
|
}
|
|
}
|
|
|
|
Rocket::Core::Context* getContext()
|
|
{
|
|
return input_context;
|
|
}
|
|
|
|
void enableInput(Rocket::Core::Context* main_ctx)
|
|
{
|
|
Assertion(main_ctx != nullptr, "Invalid context pointer detected!");
|
|
|
|
if (input_context != nullptr) {
|
|
// If we still have an active context, disable the input on the previous context
|
|
disableInput();
|
|
}
|
|
|
|
input_context = main_ctx;
|
|
input_context->AddReference();
|
|
}
|
|
void disableInput()
|
|
{
|
|
if (input_context == nullptr) {
|
|
return;
|
|
}
|
|
|
|
input_context->RemoveReference();
|
|
input_context = nullptr;
|
|
}
|
|
|
|
} // namespace scpui
|