2024-05-09 05:25:53 +00:00
|
|
|
#include <libreborn/libreborn.h>
|
|
|
|
#include <symbols/minecraft.h>
|
|
|
|
#include <GLES/gl.h>
|
|
|
|
|
2024-08-23 09:18:20 +00:00
|
|
|
#include <mods/home/home.h>
|
2024-05-09 05:25:53 +00:00
|
|
|
#include <mods/touch/touch.h>
|
2024-05-10 23:50:28 +00:00
|
|
|
#include <mods/misc/misc.h>
|
2024-05-21 23:16:19 +00:00
|
|
|
#include <mods/options/info.h>
|
2024-05-09 05:25:53 +00:00
|
|
|
|
|
|
|
#include "options-internal.h"
|
|
|
|
|
|
|
|
// Button IDs
|
|
|
|
#define DISCORD_ID 0
|
|
|
|
#define BACK_ID 1
|
|
|
|
#define INFO_ID_START 2
|
|
|
|
|
|
|
|
// Constants
|
2024-05-17 01:59:51 +00:00
|
|
|
static int line_button_padding = 8;
|
2024-05-09 05:25:53 +00:00
|
|
|
static int line_height = 8;
|
2024-05-17 01:59:51 +00:00
|
|
|
static int line_button_height = (line_button_padding * 2) + line_height;
|
|
|
|
static int padding = 4;
|
2024-05-09 05:25:53 +00:00
|
|
|
static int bottom_padding = padding;
|
|
|
|
static int inner_padding = padding;
|
|
|
|
static int title_padding = 8;
|
|
|
|
static int info_text_y_offset = (line_button_height - line_height) / 2;
|
|
|
|
static int content_y_offset_top = (title_padding * 2) + line_height;
|
|
|
|
static int content_y_offset_bottom = (bottom_padding * 2) + line_button_height;
|
|
|
|
|
|
|
|
// Extra Version Info
|
|
|
|
static std::string extra_version_info =
|
|
|
|
#ifdef MCPI_IS_APPIMAGE_BUILD
|
|
|
|
"AppImage"
|
|
|
|
#elif defined(MCPI_IS_FLATPAK_BUILD)
|
|
|
|
"Flatpak"
|
|
|
|
#else
|
|
|
|
""
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
static std::string extra_version_info_full = !extra_version_info.empty() ? (" (" + extra_version_info + ")") : "";
|
|
|
|
|
|
|
|
// Profile Directory
|
|
|
|
static std::string profile_directory_suffix =
|
|
|
|
#ifdef MCPI_IS_FLATPAK_BUILD
|
2024-06-15 12:52:15 +00:00
|
|
|
"/.var/app/" MCPI_APP_ID +
|
2024-05-09 05:25:53 +00:00
|
|
|
#endif
|
2024-06-15 12:52:15 +00:00
|
|
|
std::string(get_home_subdirectory_for_game_data())
|
2024-05-09 05:25:53 +00:00
|
|
|
;
|
|
|
|
static std::string get_profile_directory_url() {
|
2024-08-23 09:18:20 +00:00
|
|
|
std::string directory;
|
|
|
|
if (getenv(MCPI_PROFILE_DIRECTORY_ENV) != nullptr) {
|
|
|
|
// Using Custom Directory
|
|
|
|
directory = home_get();
|
|
|
|
} else {
|
|
|
|
// Determine Proper Directory
|
|
|
|
const char *home = getenv("HOME");
|
|
|
|
if (home == nullptr) {
|
|
|
|
IMPOSSIBLE();
|
|
|
|
}
|
|
|
|
directory = home + profile_directory_suffix;
|
2024-05-09 05:25:53 +00:00
|
|
|
}
|
2024-08-23 09:18:20 +00:00
|
|
|
return std::string("file://") + directory;
|
2024-05-09 05:25:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Info Data
|
|
|
|
struct info_line {
|
|
|
|
std::string (*get_text)();
|
|
|
|
std::string button_url;
|
|
|
|
std::string button_text;
|
|
|
|
};
|
|
|
|
std::string info_sound_data_state = "N/A";
|
|
|
|
static info_line info[] = {
|
|
|
|
{
|
|
|
|
.get_text = []() {
|
|
|
|
return std::string("Version: v") + reborn_get_version() + extra_version_info_full;
|
|
|
|
},
|
2024-05-21 23:16:19 +00:00
|
|
|
.button_url = MCPI_DOCUMENTATION CHANGELOG_FILE,
|
2024-05-17 01:59:51 +00:00
|
|
|
.button_text = "Changelog"
|
2024-05-09 05:25:53 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.get_text = []() {
|
|
|
|
return std::string("Profile Directory");
|
|
|
|
},
|
|
|
|
.button_url = get_profile_directory_url(),
|
|
|
|
.button_text = "Open"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.get_text = []() {
|
|
|
|
return std::string("Sound Data: ") + info_sound_data_state;
|
|
|
|
},
|
2024-05-22 06:57:00 +00:00
|
|
|
.button_url = MCPI_DOCUMENTATION "GETTING_STARTED.md#sound",
|
2024-05-17 01:59:51 +00:00
|
|
|
.button_text = "More Info"
|
2024-05-09 05:25:53 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
#define info_size int(sizeof(info) / sizeof(info_line))
|
|
|
|
|
|
|
|
// Positioned Info
|
|
|
|
struct info_pos {
|
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
};
|
|
|
|
struct info_line_position {
|
|
|
|
info_pos text;
|
|
|
|
info_pos button;
|
|
|
|
};
|
|
|
|
static info_line_position positioned_info[info_size];
|
|
|
|
static int content_height = 0;
|
2024-05-17 01:59:51 +00:00
|
|
|
static int line_button_width = 0;
|
2024-05-09 05:25:53 +00:00
|
|
|
static void position_info(Font *font, int width, int height) {
|
|
|
|
// First Stage (Find Max Text Width)
|
|
|
|
int info_text_width = 0;
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
std::string text = info[i].get_text();
|
2024-07-15 07:05:05 +00:00
|
|
|
int text_width = font->width(text);
|
2024-05-09 05:25:53 +00:00
|
|
|
if (text_width > info_text_width) {
|
|
|
|
info_text_width = text_width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Second Stage (Initial Positioning)
|
|
|
|
int y = 0;
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
// Padding
|
|
|
|
if (i != 0) {
|
|
|
|
y += padding;
|
|
|
|
}
|
|
|
|
// Y
|
|
|
|
positioned_info[i].button.y = y;
|
|
|
|
positioned_info[i].text.y = y + info_text_y_offset;
|
|
|
|
// X
|
|
|
|
positioned_info[i].button.x = info_text_width + padding;
|
|
|
|
positioned_info[i].text.x = 0;
|
|
|
|
// Advance
|
|
|
|
y += line_button_height;
|
|
|
|
}
|
|
|
|
|
2024-05-17 01:59:51 +00:00
|
|
|
// Third Stage (Find Line Button Width)
|
|
|
|
line_button_width = 0;
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
2024-09-21 01:30:47 +00:00
|
|
|
const int text_width = font->width(info[i].button_text);
|
2024-05-17 01:59:51 +00:00
|
|
|
if (text_width > line_button_width) {
|
|
|
|
line_button_width = text_width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
line_button_width += line_button_padding * 2;
|
|
|
|
|
|
|
|
// Fourth Stage (Centering)
|
2024-09-21 01:30:47 +00:00
|
|
|
const int info_height = y;
|
|
|
|
const int info_width = info_text_width + padding + line_button_width;
|
2024-05-09 05:25:53 +00:00
|
|
|
content_height = height - content_y_offset_top - content_y_offset_bottom;
|
2024-09-21 01:30:47 +00:00
|
|
|
const int info_y_offset = ((content_height - info_height) / 2) + content_y_offset_top;
|
|
|
|
const int info_x_offset = (width - info_width) / 2;
|
2024-05-09 05:25:53 +00:00
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
positioned_info[i].button.x += info_x_offset;
|
|
|
|
positioned_info[i].button.y += info_y_offset;
|
|
|
|
positioned_info[i].text.x += info_x_offset;
|
|
|
|
positioned_info[i].text.y += info_y_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open URL
|
2024-05-21 23:16:19 +00:00
|
|
|
void open_url(const std::string &url) {
|
2024-05-09 05:25:53 +00:00
|
|
|
int return_code;
|
|
|
|
const char *command[] = {"xdg-open", url.c_str(), nullptr};
|
|
|
|
char *output = run_command(command, &return_code, nullptr);
|
|
|
|
if (output != nullptr) {
|
|
|
|
free(output);
|
|
|
|
}
|
|
|
|
if (!is_exit_status_success(return_code)) {
|
|
|
|
WARN("Unable To Open URL: %s", url.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create VTable
|
|
|
|
CUSTOM_VTABLE(info_screen, Screen) {
|
|
|
|
// Buttons
|
|
|
|
static Button *discord;
|
|
|
|
static Button *back;
|
|
|
|
static Button *info_buttons[info_size];
|
|
|
|
// Init
|
|
|
|
vtable->init = [](Screen *self) {
|
|
|
|
// Info
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
Button *button = touch_create_button(INFO_ID_START + i, info[i].button_text);
|
|
|
|
self->rendered_buttons.push_back(button);
|
|
|
|
self->selectable_buttons.push_back(button);
|
|
|
|
info_buttons[i] = button;
|
|
|
|
}
|
|
|
|
// Discord Button
|
|
|
|
discord = touch_create_button(DISCORD_ID, "Discord");
|
|
|
|
self->rendered_buttons.push_back(discord);
|
|
|
|
self->selectable_buttons.push_back(discord);
|
|
|
|
// Back Button
|
|
|
|
back = touch_create_button(BACK_ID, "Back");
|
|
|
|
self->rendered_buttons.push_back(back);
|
|
|
|
self->selectable_buttons.push_back(back);
|
|
|
|
};
|
|
|
|
// Handle Back
|
2024-09-21 01:30:47 +00:00
|
|
|
vtable->handleBackEvent = [](Screen *self, const bool do_nothing) {
|
2024-05-09 05:25:53 +00:00
|
|
|
if (!do_nothing) {
|
2024-09-21 01:30:47 +00:00
|
|
|
OptionsScreen *screen = OptionsScreen::allocate();
|
2024-05-09 05:25:53 +00:00
|
|
|
ALLOC_CHECK(screen);
|
|
|
|
screen->constructor();
|
|
|
|
self->minecraft->setScreen((Screen *) screen);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
// Rendering
|
|
|
|
static Screen_render_t original_render = vtable->render;
|
2024-09-21 01:30:47 +00:00
|
|
|
vtable->render = [](Screen *self, const int x, const int y, const float param_1) {
|
2024-05-09 05:25:53 +00:00
|
|
|
// Background
|
2024-05-10 23:50:28 +00:00
|
|
|
misc_render_background(80, self->minecraft, 0, 0, self->width, self->height);
|
|
|
|
misc_render_background(32, self->minecraft, 0, content_y_offset_top, self->width, content_height);
|
2024-05-09 05:25:53 +00:00
|
|
|
// Call Original Method
|
|
|
|
original_render(self, x, y, param_1);
|
|
|
|
// Title
|
|
|
|
std::string title = "Reborn Information";
|
2024-07-15 07:05:05 +00:00
|
|
|
self->drawCenteredString(self->font, title, self->width / 2, title_padding, 0xffffffff);
|
2024-05-09 05:25:53 +00:00
|
|
|
// Info Text
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
std::string text = info[i].get_text();
|
2024-07-15 07:05:05 +00:00
|
|
|
self->drawString(self->font, text, positioned_info[i].text.x, positioned_info[i].text.y, 0xffffffff);
|
2024-05-09 05:25:53 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
// Positioning
|
|
|
|
vtable->setupPositions = [](Screen *self) {
|
|
|
|
// Height/Width
|
2024-09-21 01:30:47 +00:00
|
|
|
constexpr int width = 120;
|
2024-05-09 05:25:53 +00:00
|
|
|
discord->width = back->width = width;
|
|
|
|
discord->height = back->height = line_button_height;
|
|
|
|
// X/Y
|
|
|
|
discord->y = back->y = self->height - bottom_padding - line_button_height;
|
|
|
|
discord->x = (self->width / 2) - inner_padding - width;
|
|
|
|
back->x = (self->width / 2) + inner_padding;
|
|
|
|
// Info
|
|
|
|
position_info(self->font, self->width, self->height);
|
|
|
|
for (int i = 0; i < info_size; i++) {
|
|
|
|
Button *button = info_buttons[i];
|
|
|
|
button->width = line_button_width;
|
|
|
|
button->height = line_button_height;
|
|
|
|
button->x = positioned_info[i].button.x;
|
|
|
|
button->y = positioned_info[i].button.y;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Cleanup
|
|
|
|
vtable->removed = [](Screen *self) {
|
|
|
|
for (Button *button : self->rendered_buttons) {
|
|
|
|
button->destructor_deleting();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// Handle Button Click
|
|
|
|
vtable->buttonClicked = [](Screen *self, Button *button) {
|
|
|
|
if (button->id == BACK_ID) {
|
|
|
|
// Back
|
|
|
|
self->handleBackEvent(false);
|
|
|
|
} else if (button->id == DISCORD_ID) {
|
|
|
|
// Open Discord Invite
|
2024-05-12 07:19:01 +00:00
|
|
|
open_url(MCPI_DISCORD_INVITE);
|
2024-05-09 05:25:53 +00:00
|
|
|
} else if (button->id >= INFO_ID_START) {
|
|
|
|
// Open Info URL
|
2024-09-21 01:30:47 +00:00
|
|
|
const int i = button->id - INFO_ID_START;
|
2024-05-09 05:25:53 +00:00
|
|
|
open_url(info[i].button_url);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create Screen
|
|
|
|
Screen *_create_options_info_screen() {
|
|
|
|
// Allocate
|
2024-09-21 01:30:47 +00:00
|
|
|
Screen *screen = Screen::allocate();
|
2024-05-09 05:25:53 +00:00
|
|
|
ALLOC_CHECK(screen);
|
|
|
|
screen->constructor();
|
|
|
|
|
|
|
|
// Set VTable
|
|
|
|
screen->vtable = get_info_screen_vtable();
|
|
|
|
|
|
|
|
// Return
|
|
|
|
return screen;
|
2024-05-15 09:02:19 +00:00
|
|
|
}
|