forked from mirrors/principia
420 lines
11 KiB
C++
420 lines
11 KiB
C++
#ifdef PRINCIPIA_BACKEND_IMGUI
|
|
|
|
#include "ui_imgui.hh"
|
|
|
|
#include "game.hh"
|
|
#include "main.hh"
|
|
#include "misc.hh"
|
|
#include "settings.hh"
|
|
#include "ui.hh"
|
|
|
|
#include "tms/backend/print.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <SDL.h>
|
|
#include <SDL_opengl.h>
|
|
#include <SDL_syswm.h>
|
|
|
|
#include "ui_imgui_impl_tms.hh"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
// Misc helper functions
|
|
|
|
ImVec4 rgba(uint32_t color) {
|
|
float components[4]; //ABGR
|
|
for (int i = 0; i < 4; i++) {
|
|
components[i] = (float)(color & 0xFF) / 255.;
|
|
color >>= 8;
|
|
}
|
|
return ImVec4(components[3], components[2], components[1], components[0]);
|
|
}
|
|
|
|
bool lax_search(const std::string& where, const std::string& what) {
|
|
return std::search(
|
|
where.begin(), where.end(),
|
|
what.begin(), what.end(),
|
|
[](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }
|
|
) != where.end();
|
|
}
|
|
|
|
void ImGui_CenterNextWindow() {
|
|
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
|
}
|
|
|
|
void ImGui_BeginScaleFont(float scale) {
|
|
ImGui::GetFont()->Scale = scale;
|
|
ImGui::PushFont(ImGui::GetFont());
|
|
}
|
|
|
|
void ImGui_EndScaleFont() {
|
|
ImGui::GetFont()->Scale = 1.;
|
|
ImGui::PopFont();
|
|
ImGui::GetFont()->Scale = 1.;
|
|
}
|
|
|
|
void handle_do_open(bool *do_open, const char* name) {
|
|
if (*do_open) {
|
|
*do_open = false;
|
|
ImGui::OpenPopup(name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// FILE LOADING //
|
|
std::vector<uint8_t> *load_ass(const char *path) {
|
|
tms_infof("(imgui-backend) loading asset from %s...", path);
|
|
|
|
FILE_IN_ASSET(true);
|
|
FILE *file = (FILE*) _fopen(path, "rb");
|
|
tms_assertf(file, "file not found");
|
|
|
|
_fseek(file, 0, SEEK_END);
|
|
size_t size = _ftell(file);
|
|
tms_debugf("buf size %d", (int) size);
|
|
void *buffer = malloc(size + 1);
|
|
|
|
_fseek(file, 0, SEEK_SET);
|
|
_fread(buffer, 1, size, file);
|
|
_fclose(file);
|
|
|
|
uint8_t *typed_buffer = (uint8_t*) buffer;
|
|
std::vector<uint8_t> *vec = new std::vector<uint8_t>(typed_buffer, typed_buffer + size);
|
|
free(buffer);
|
|
|
|
return vec;
|
|
}
|
|
|
|
/// PFONT ///
|
|
|
|
static struct PFont im_load_ttf(const char *path, float size_pixels) {
|
|
std::vector<uint8_t>* buf = load_ass(path);
|
|
|
|
ImFontConfig font_cfg;
|
|
font_cfg.FontDataOwnedByAtlas = false;
|
|
if (size_pixels <= 16.) {
|
|
font_cfg.OversampleH = 3;
|
|
}
|
|
|
|
ImFont *font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(buf->data(), buf->size(), size_pixels, &font_cfg);
|
|
|
|
struct PFont pfont;
|
|
pfont.fontbuffer = buf;
|
|
pfont.font = font;
|
|
|
|
return pfont;
|
|
}
|
|
|
|
struct PFont ui_font;
|
|
struct PFont ui_font_mono;
|
|
|
|
static void load_fonts() {
|
|
//TODO free existing fonts
|
|
|
|
float size_pixels = 12.f;
|
|
size_pixels *= settings["uiscale"]->v.f;
|
|
size_pixels = roundf(size_pixels);
|
|
|
|
tms_infof("font size %fpx", size_pixels);
|
|
|
|
ui_font = im_load_ttf("data/fonts/Roboto-Bold.ttf", size_pixels);
|
|
ui_font_mono = im_load_ttf("data/fonts/SourceCodePro-Medium.ttf", size_pixels + 2);
|
|
|
|
}
|
|
|
|
static void update_imgui_ui_scale() {
|
|
float scale_factor = settings["uiscale"]->v.f;
|
|
ImGui::GetStyle().ScaleAllSizes(scale_factor);
|
|
|
|
//ImGui::GetIO().FontGlobalScale = roundf(9. * scale_factor) / 9.;
|
|
}
|
|
|
|
static void principia_style() {
|
|
ImGui::StyleColorsDark();
|
|
ImGuiStyle *style = &ImGui::GetStyle();
|
|
ImVec4* colors = style->Colors;
|
|
|
|
//Rounding
|
|
style->FramePadding = ImVec2(10, 5);
|
|
style->ItemSpacing = ImVec2(8, 6);
|
|
style->FrameRounding = style->GrabRounding = 2.3f;
|
|
style->WindowRounding = style->PopupRounding = style->ChildRounding = 3.0f;
|
|
|
|
//style->FrameBorderSize = .5;
|
|
|
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.35f);
|
|
}
|
|
|
|
static bool init_ready = false;
|
|
|
|
// UI helpers implemented here to avoid multiple definitions when header is included
|
|
void ui_init() {
|
|
UiLevelManager::init();
|
|
UiLuaEditor::init();
|
|
//UiQuickadd::init();
|
|
UiSynthesizer::init();
|
|
}
|
|
|
|
//On debug builds, open imgui demo window by pressing Shift+F9
|
|
#ifdef DEBUG
|
|
static bool show_demo = false;
|
|
static void ui_demo_layout() {
|
|
if (ImGui::IsKeyPressed(ImGuiKey::ImGuiKey_F9) && ImGui::GetIO().KeyShift) {
|
|
show_demo ^= 1;
|
|
}
|
|
if (show_demo) {
|
|
ImGui::ShowDemoWindow(&show_demo);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void ui_layout() {
|
|
#ifdef DEBUG
|
|
ui_demo_layout();
|
|
#endif
|
|
UiSandboxMenu::layout();
|
|
UiPlayMenu::layout();
|
|
UiLevelManager::layout();
|
|
UiVariable::layout();
|
|
UiLogin::layout();
|
|
UiMessage::layout();
|
|
UiSettings::layout();
|
|
UiLuaEditor::layout();
|
|
UiTips::layout();
|
|
UiSandboxMode::layout();
|
|
UiQuickadd::layout();
|
|
UiSynthesizer::layout();
|
|
UiObjColorPicker::layout();
|
|
UiLevelProperties::layout();
|
|
UiSave::layout();
|
|
UiNewLevel::layout();
|
|
UiFrequency::layout();
|
|
UiConfirm::layout();
|
|
UiAnimal::layout();
|
|
UiRobot::layout();
|
|
UiSticky::layout();
|
|
UiTreasureChest::layout();
|
|
UiPolygon::layout();
|
|
UiRubber::layout();
|
|
UiDecoration::layout();
|
|
}
|
|
|
|
// Non-header ui::* definitions
|
|
void ui::init() {
|
|
tms_assertf(!init_ready, "ui::init called twice");
|
|
|
|
//create context
|
|
#ifdef DEBUG
|
|
IMGUI_CHECKVERSION();
|
|
#endif
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
//set flags
|
|
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NavEnableKeyboard;
|
|
//io.ConfigInputTrickleEventQueue = false;
|
|
io.ConfigWindowsResizeFromEdges = true; //XXX: not active until custom cursors are implemented...
|
|
io.ConfigDragClickToInputText = true;
|
|
//Disable saving state/logging
|
|
io.IniFilename = NULL;
|
|
io.LogFilename = NULL;
|
|
|
|
//style
|
|
principia_style();
|
|
|
|
//update scale
|
|
update_imgui_ui_scale();
|
|
|
|
//load fonts
|
|
load_fonts();
|
|
|
|
//ensure gl ctx exists
|
|
tms_assertf(_tms._window != NULL, "window does not exist yet");
|
|
tms_assertf(SDL_GL_GetCurrentContext() != NULL, "no gl ctx");
|
|
|
|
//init
|
|
if (!ImGui_ImplOpenGL3_Init()) {
|
|
tms_fatalf("(imgui-backend) gl impl init failed");
|
|
}
|
|
|
|
if (ImGui_ImplTMS_Init() != T_OK) {
|
|
tms_fatalf("(imgui-backend) tms impl init failed");
|
|
}
|
|
|
|
//call ui_init
|
|
ui_init();
|
|
|
|
init_ready = true;
|
|
}
|
|
|
|
void ui::render() {
|
|
if (settings["render_gui"]->is_false()) return;
|
|
|
|
tms_assertf(init_ready, "ui::render called before ui::init");
|
|
tms_assertf(GImGui != NULL, "gimgui is null. is imgui ready?");
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
//start frame
|
|
if (ImGui_ImplTms_NewFrame() <= 0) return;
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::PushFont(ui_font.font);
|
|
|
|
//layout
|
|
ui_layout();
|
|
|
|
ImGui::PopFont();
|
|
|
|
//render
|
|
ImGui::Render();
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
}
|
|
|
|
void ui::open_dialog(int num, void *data) {
|
|
tms_assertf(init_ready, "ui::open_dialog called before ui::init");
|
|
switch (num) {
|
|
//XXX: this gets called after opening the sandbox menu, closing it immediately
|
|
case CLOSE_ABSOLUTELY_ALL_DIALOGS:
|
|
case CLOSE_ALL_DIALOGS:
|
|
tms_infof("XXX: CLOSE_ALL_DIALOGS/CLOSE_ABSOLUTELY_ALL_DIALOGS (200/201) are intentionally ignored");
|
|
break;
|
|
case DIALOG_SANDBOX_MENU:
|
|
UiSandboxMenu::open();
|
|
break;
|
|
case DIALOG_PLAY_MENU:
|
|
UiPlayMenu::open();
|
|
break;
|
|
case DIALOG_OPEN:
|
|
UiLevelManager::open();
|
|
break;
|
|
case DIALOG_VARIABLE:
|
|
UiVariable::open();
|
|
break;
|
|
case DIALOG_LOGIN:
|
|
UiLogin::open();
|
|
break;
|
|
case DIALOG_SETTINGS:
|
|
UiSettings::open();
|
|
break;
|
|
case DIALOG_ESCRIPT:
|
|
UiLuaEditor::open();
|
|
break;
|
|
case DIALOG_SANDBOX_MODE:
|
|
UiSandboxMode::open();
|
|
break;
|
|
case DIALOG_QUICKADD:
|
|
UiQuickadd::open();
|
|
break;
|
|
case DIALOG_SYNTHESIZER:
|
|
UiSynthesizer::open();
|
|
break;
|
|
case DIALOG_BEAM_COLOR:
|
|
case DIALOG_POLYGON_COLOR:
|
|
case DIALOG_PIXEL_COLOR:
|
|
UiObjColorPicker::open();
|
|
break;
|
|
case DIALOG_LEVEL_PROPERTIES:
|
|
UiLevelProperties::open();
|
|
break;
|
|
case DIALOG_SAVE:
|
|
case DIALOG_SAVE_COPY:
|
|
UiSave::open();
|
|
break;
|
|
case DIALOG_NEW_LEVEL:
|
|
UiNewLevel::open();
|
|
break;
|
|
case DIALOG_SET_FREQUENCY:
|
|
UiFrequency::open(false);
|
|
break;
|
|
case DIALOG_SET_FREQ_RANGE:
|
|
UiFrequency::open(true);
|
|
break;
|
|
case DIALOG_LEVEL_INFO:
|
|
UiMessage::open((char *)data, MessageType::LevelInfo);
|
|
break;
|
|
case DIALOG_ANIMAL:
|
|
UiAnimal::open();
|
|
break;
|
|
case DIALOG_ROBOT:
|
|
UiRobot::open();
|
|
break;
|
|
case DIALOG_STICKY:
|
|
UiSticky::open();
|
|
break;
|
|
case DIALOG_TREASURE_CHEST:
|
|
UiTreasureChest::open();
|
|
break;
|
|
case DIALOG_POLYGON:
|
|
UiPolygon::open();
|
|
break;
|
|
case DIALOG_RUBBER:
|
|
UiRubber::open();
|
|
break;
|
|
case DIALOG_DECORATION:
|
|
UiDecoration::open();
|
|
break;
|
|
default:
|
|
tms_errorf("dialog %d not implemented yet", num);
|
|
}
|
|
}
|
|
|
|
void ui::open_sandbox_tips() {
|
|
UiTips::open();
|
|
}
|
|
|
|
void ui::emit_signal(int num, void *data){
|
|
switch (num) {
|
|
case SIGNAL_LOGIN_SUCCESS:
|
|
UiLogin::complete_login(num);
|
|
if (ui::next_action != ACTION_IGNORE) {
|
|
P.add_action(ui::next_action, 0);
|
|
ui::next_action = ACTION_IGNORE;
|
|
}
|
|
break;
|
|
case SIGNAL_LOGIN_FAILED:
|
|
ui::next_action = ACTION_IGNORE;
|
|
UiLogin::complete_login(num);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ui::quit() {
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplTMS_Shutdown();
|
|
ImGui::DestroyContext();
|
|
}
|
|
|
|
void ui::set_next_action(int action_id) {
|
|
tms_infof("set_next_action %d", action_id);
|
|
ui::next_action = action_id;
|
|
}
|
|
|
|
void ui::open_error_dialog(const char *error_msg) {
|
|
UiMessage::open(error_msg, MessageType::Error);
|
|
}
|
|
|
|
void ui::confirm(
|
|
const char *text,
|
|
const char *button1, principia_action action1,
|
|
const char *button2, principia_action action2,
|
|
const char *button3, principia_action action3,
|
|
struct confirm_data _confirm_data
|
|
) {
|
|
UiConfirm::open(text, button1, action1, button2, action2, button3, action3, _confirm_data);
|
|
}
|
|
|
|
void ui::alert(const char* text, uint8_t type) {
|
|
UiMessage::open(text, MessageType::Message);
|
|
}
|
|
|
|
#endif
|