From 58713976d4c3174949dc7deced1879fd8eff2a1f Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Thu, 1 Feb 2024 03:12:24 -0500 Subject: [PATCH] In-Game Chat --- media-layer/core/src/media.c | 2 + media-layer/include/SDL/SDL_keysym.h | 1 + mods/CMakeLists.txt | 5 +- mods/include/mods/chat/chat.h | 1 - mods/include/mods/misc/misc.h | 2 + .../mods/text-input-box/TextInputBox.h | 35 +++ .../mods/text-input-box/TextInputScreen.h | 12 ++ mods/src/chat/chat-internal.h | 7 +- mods/src/chat/chat.cpp | 34 +-- mods/src/chat/ui.c | 83 -------- mods/src/chat/ui.cpp | 98 +++++++++ mods/src/compat/compat.c | 7 +- mods/src/input/misc.c | 3 +- mods/src/misc/misc.c | 3 +- mods/src/sign/sign.cpp | 36 +--- mods/src/text-input-box/README.md | 2 + mods/src/text-input-box/TextInputBox.cpp | 200 ++++++++++++++++++ mods/src/text-input-box/TextInputScreen.cpp | 40 ++++ symbols/CMakeLists.txt | 3 +- symbols/src/game/Minecraft.def | 1 + symbols/src/gui/Font.def | 1 + symbols/src/gui/{components => }/Gui.def | 0 symbols/src/gui/components/GuiComponent.def | 7 +- symbols/src/gui/screens/Screen.def | 11 +- .../Touch_IngameBlockSelectionScreen.def | 3 +- symbols/src/input/Keyboard.def | 1 + 26 files changed, 446 insertions(+), 152 deletions(-) create mode 100644 mods/include/mods/text-input-box/TextInputBox.h create mode 100644 mods/include/mods/text-input-box/TextInputScreen.h delete mode 100644 mods/src/chat/ui.c create mode 100644 mods/src/chat/ui.cpp create mode 100644 mods/src/text-input-box/README.md create mode 100644 mods/src/text-input-box/TextInputBox.cpp create mode 100644 mods/src/text-input-box/TextInputScreen.cpp rename symbols/src/gui/{components => }/Gui.def (100%) create mode 100644 symbols/src/input/Keyboard.def diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index bfe12cfb..759c01ef 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -123,6 +123,8 @@ static SDLKey glfw_key_to_sdl_key(int key) { return SDLK_RETURN; case GLFW_KEY_BACKSPACE: return SDLK_BACKSPACE; + case GLFW_KEY_DELETE: + return SDLK_DELETE; // Fullscreen case GLFW_KEY_F11: return SDLK_F11; diff --git a/media-layer/include/SDL/SDL_keysym.h b/media-layer/include/SDL/SDL_keysym.h index a6397c52..e4ef34e0 100644 --- a/media-layer/include/SDL/SDL_keysym.h +++ b/media-layer/include/SDL/SDL_keysym.h @@ -28,6 +28,7 @@ typedef enum { SDLK_s = 115, SDLK_t = 116, SDLK_w = 119, + SDLK_DELETE = 127, SDLK_UP = 273, SDLK_DOWN = 274, SDLK_RIGHT = 275, diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index b367523f..a2e620fc 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -15,7 +15,7 @@ set(SRC src/version/version.cpp # chat src/chat/chat.cpp - src/chat/ui.c + src/chat/ui.cpp # creative src/creative/creative.cpp # game-mode @@ -96,6 +96,9 @@ else() # textures src/textures/textures.cpp src/textures/lava.cpp + # text-input-box + src/text-input-box/TextInputBox.cpp + src/text-input-box/TextInputScreen.cpp ) endif() diff --git a/mods/include/mods/chat/chat.h b/mods/include/mods/chat/chat.h index e9d4ad87..3ff276c3 100644 --- a/mods/include/mods/chat/chat.h +++ b/mods/include/mods/chat/chat.h @@ -15,7 +15,6 @@ extern "C" { #ifndef MCPI_SERVER_MODE void chat_open(); -unsigned int chat_get_counter(); #endif // Override using the HOOK() macro to provide customized chat behavior. diff --git a/mods/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index 291384bd..06b8a786 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -28,6 +28,8 @@ void Level_saveLevelData_injection(Level *level); // Use this instead of directly calling Gui::addMessage(), it has proper logging! void misc_add_message(Gui *gui, const char *text); +extern bool is_in_chat; + #ifdef __cplusplus } #endif diff --git a/mods/include/mods/text-input-box/TextInputBox.h b/mods/include/mods/text-input-box/TextInputBox.h new file mode 100644 index 00000000..a75f594f --- /dev/null +++ b/mods/include/mods/text-input-box/TextInputBox.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +struct TextInputBox { + GuiComponent super; + + int m_ID; + int m_xPos; + int m_yPos; + int m_width; + int m_height; + std::string m_placeholder; + std::string m_text; + bool m_bFocused; + bool m_bEnabled; + bool m_bCursorOn; + int m_insertHead; + int m_lastFlashed; + Font *m_pFont; + int m_maxLength; + + void setSize(int x, int y, int width = 200, int height = 12); + void init(Font *pFont); + void setEnabled(bool bEnabled); + void keyPressed(int key); + void charPressed(int chr); + void render(); + void tick(); + void setFocused(bool b); + void onClick(int x, int y); + bool clicked(int x, int y); + + static TextInputBox create(int id, const std::string &placeholder = "", const std::string &text = ""); +}; diff --git a/mods/include/mods/text-input-box/TextInputScreen.h b/mods/include/mods/text-input-box/TextInputScreen.h new file mode 100644 index 00000000..f076d30b --- /dev/null +++ b/mods/include/mods/text-input-box/TextInputScreen.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +struct TextInputScreen { + Screen super; + std::vector m_textInputs; + + static void setup(Screen_vtable *vtable); +}; diff --git a/mods/src/chat/chat-internal.h b/mods/src/chat/chat-internal.h index 436d6b49..293c6a0f 100644 --- a/mods/src/chat/chat-internal.h +++ b/mods/src/chat/chat-internal.h @@ -6,9 +6,12 @@ extern "C" { #endif -__attribute__((visibility("internal"))) extern int _chat_enabled; #ifndef MCPI_SERVER_MODE -__attribute__((visibility("internal"))) void _chat_queue_message(char *message); +__attribute__((visibility("internal"))) void _chat_queue_message(const char *message); +#endif + +#ifndef MCPI_HEADLESS_MODE +__attribute__((visibility("internal"))) void _init_chat_ui(); #endif #ifdef __cplusplus diff --git a/mods/src/chat/chat.cpp b/mods/src/chat/chat.cpp index 37bc6a4c..1fef0d43 100644 --- a/mods/src/chat/chat.cpp +++ b/mods/src/chat/chat.cpp @@ -5,9 +5,6 @@ #include #include #include -#ifndef MCPI_HEADLESS_MODE -#include -#endif #include #ifndef MCPI_HEADLESS_MODE @@ -22,9 +19,6 @@ #include "chat-internal.h" #include -// Store If Chat is Enabled -int _chat_enabled = 0; - // Message Limitations #define MAX_CHAT_MESSAGE_LENGTH 512 @@ -95,45 +89,27 @@ static void ServerSideNetworkHandler_handle_ChatPacket_injection(ServerSideNetwo #ifndef MCPI_HEADLESS_MODE // Message Queue -static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; static std::vector queue; // Add To Queue -void _chat_queue_message(char *message) { - // Lock - pthread_mutex_lock(&queue_mutex); +void _chat_queue_message(const char *message) { // Add - std::string str; - str.append(message); + std::string str = message; queue.push_back(str); - // Unlock - pthread_mutex_unlock(&queue_mutex); } // Empty Queue unsigned int old_chat_counter = 0; static void send_queued_messages(Minecraft *minecraft) { - // Lock - pthread_mutex_lock(&queue_mutex); - // If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor - unsigned int new_chat_counter = chat_get_counter(); - if (old_chat_counter > new_chat_counter && new_chat_counter == 0) { - // Unlock UI - media_set_interactable(1); - } - old_chat_counter = new_chat_counter; // Loop for (unsigned int i = 0; i < queue.size(); i++) { send_api_chat_command(minecraft, (char *) queue[i].c_str()); } queue.clear(); - // Unlock - pthread_mutex_unlock(&queue_mutex); } #endif // Init void init_chat() { - _chat_enabled = feature_has("Implement Chat", server_enabled); - if (_chat_enabled) { + if (feature_has("Implement Chat", server_enabled)) { // Disable Original ChatPacket Loopback unsigned char disable_chat_packet_loopback_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" patch((void *) 0x6b490, disable_chat_packet_loopback_patch); @@ -141,9 +117,11 @@ void init_chat() { overwrite_call((void *) 0x6b518, (void *) CommandServer_parse_CommandServer_dispatchPacket_injection); // Re-Broadcast ChatPacket patch_address(ServerSideNetworkHandler_handle_ChatPacket_vtable_addr, (void *) ServerSideNetworkHandler_handle_ChatPacket_injection); - // Send Messages On Input Tick #ifndef MCPI_HEADLESS_MODE + // Send Messages On Input Tick input_run_on_tick(send_queued_messages); + // Init UI + _init_chat_ui(); #endif } } diff --git a/mods/src/chat/ui.c b/mods/src/chat/ui.c deleted file mode 100644 index 04c14da8..00000000 --- a/mods/src/chat/ui.c +++ /dev/null @@ -1,83 +0,0 @@ -// Config Needs To Load First -#include - -// Chat UI Code Is Useless In Headless Mode -#ifndef MCPI_HEADLESS_MODE - -#include -#include -#include -#include - -#include - -#include "chat-internal.h" -#include - -// Count Chat Windows -static pthread_mutex_t chat_counter_lock = PTHREAD_MUTEX_INITIALIZER; -static volatile unsigned int chat_counter = 0; -unsigned int chat_get_counter() { - return chat_counter; -} - -// Chat Thread -#define DIALOG_TITLE "Chat" -static void *chat_thread(__attribute__((unused)) void *nop) { - // Open - int return_code; - const char *command[] = { - "zenity", - "--title", DIALOG_TITLE, - "--name", MCPI_APP_ID, - "--entry", - "--text", "Enter Chat Message:", - NULL - }; - char *output = run_command(command, &return_code, NULL); - // Handle Message - if (output != NULL) { - // Check Return Code - if (is_exit_status_success(return_code)) { - // Remove Ending Newline - int length = strlen(output); - if (output[length - 1] == '\n') { - output[length - 1] = '\0'; - } - length = strlen(output); - // Don't Allow Empty Strings - if (length > 0) { - // Submit - char *safe_output = to_cp437(output); - _chat_queue_message(safe_output); - free(safe_output); - } - } - // Free Output - free(output); - } - // Update Counter - pthread_mutex_lock(&chat_counter_lock); - chat_counter--; - pthread_mutex_unlock(&chat_counter_lock); - // Return - return NULL; -} - -// Create Chat Thead -void chat_open() { - if (_chat_enabled) { - // Lock UI - media_set_interactable(0); - - // Update Counter - pthread_mutex_lock(&chat_counter_lock); - chat_counter++; - pthread_mutex_unlock(&chat_counter_lock); - // Start Thread - pthread_t thread; - pthread_create(&thread, NULL, chat_thread, NULL); - } -} - -#endif diff --git a/mods/src/chat/ui.cpp b/mods/src/chat/ui.cpp new file mode 100644 index 00000000..5dd304c4 --- /dev/null +++ b/mods/src/chat/ui.cpp @@ -0,0 +1,98 @@ +// Config Needs To Load First +#include + +// Chat UI Code Is Useless In Headless Mode +#ifndef MCPI_HEADLESS_MODE + +#include "chat-internal.h" +#include +#include +#include + +// Structure +struct ChatScreen { + TextInputScreen super; + TextInputBox chat; +}; +CUSTOM_VTABLE(chat_screen, Screen) { + TextInputScreen::setup(vtable); + // Init + vtable->init = [](Screen *super) { + Screen_init_non_virtual(super); + ChatScreen *self = (ChatScreen *) super; + self->super.m_textInputs.push_back(&self->chat); + self->chat.init(super->font); + self->chat.setFocused(true); + is_in_chat = true; + }; + // Removal + vtable->removed = [](Screen *super) { + Screen_removed_non_virtual(super); + is_in_chat = false; + }; + // Rendering + static Screen_render_t original_render = vtable->render; + vtable->render = [](Screen *super, int x, int y, float param_1) { + // Background + super->vtable->renderBackground(super); + // Render Chat + Gui_renderChatMessages(&super->minecraft->gui, super->height, 20, true, super->font); + // Call Original Method + original_render(super, x, y, param_1); + }; + // Positioning + vtable->setupPositions = [](Screen *super) { + Screen_setupPositions_non_virtual(super); + ChatScreen *self = (ChatScreen *) super; + int height = 20; + int x = 0; + int y = super->height - height; + int width = super->width; + self->chat.setSize(x, y, width, height); + }; + // Key Presses + static Screen_keyPressed_t original_keyPressed = vtable->keyPressed; + vtable->keyPressed = [](Screen *super, int key) { + // Handle Enter + ChatScreen *self = (ChatScreen *) super; + if (key == 0x0d) { + _chat_queue_message(self->chat.m_text.c_str()); + Minecraft_setScreen(super->minecraft, NULL); + } + // Call Original Method + original_keyPressed(super, key); + }; +} +static Screen *create_chat_screen() { + // Construct + ChatScreen *screen = new ChatScreen; + ALLOC_CHECK(screen); + Screen_constructor(&screen->super.super); + + // Set VTable + screen->super.super.vtable = get_chat_screen_vtable(); + + // Setup + screen->chat = TextInputBox::create(0); + + // Return + return (Screen *) screen; +} + +// Open Screen +static bool open_chat_screen = false; +void chat_open() { + open_chat_screen = true; +} + +// Init +void _init_chat_ui() { + misc_run_on_tick([](Minecraft *minecraft) { + if (open_chat_screen && Minecraft_isLevelGenerated(minecraft) && minecraft->screen == NULL) { + Minecraft_setScreen(minecraft, create_chat_screen()); + } + open_chat_screen = false; + }); +} + +#endif diff --git a/mods/src/compat/compat.c b/mods/src/compat/compat.c index 9e4e831c..aa8f3513 100644 --- a/mods/src/compat/compat.c +++ b/mods/src/compat/compat.c @@ -70,12 +70,7 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) { input_third_person(); handled = 1; } else if (event->key.keysym.sym == SDLK_t) { - // Only When In-Game With No Other Chat Windows Open - if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON && chat_get_counter() == 0) { - // Open Chat - chat_open(); - } - // Mark Handled + chat_open(); handled = 1; } else if (event->key.keysym.sym == SDLK_ESCAPE) { // Treat Escape As Back Button Press (This Fixes Issues With Signs) diff --git a/mods/src/input/misc.c b/mods/src/input/misc.c index d63f3106..1dc74f6c 100644 --- a/mods/src/input/misc.c +++ b/mods/src/input/misc.c @@ -80,7 +80,8 @@ static void _handle_mouse_grab(Minecraft *minecraft) { // Block UI Interaction When Mouse Is Locked static bool Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection(Minecraft *minecraft) { - if (!enable_misc || SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) { + bool is_in_game = minecraft->screen == NULL || minecraft->screen->vtable == (Screen_vtable *) Touch_IngameBlockSelectionScreen_vtable_base; + if (!enable_misc || (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF && is_in_game)) { // Call Original Method return creative_is_restricted() && Minecraft_isCreativeMode(minecraft); } else { diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 78631c88..3b5bba40 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -65,6 +65,7 @@ static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_ // Additional GUI Rendering static int hide_chat_messages = 0; +bool is_in_chat = 0; static int render_selected_item_text = 0; static void Gui_renderChatMessages_injection(Gui *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, Font *font) { // Handle Classic HUD @@ -76,7 +77,7 @@ static void Gui_renderChatMessages_injection(Gui *gui, int32_t y_offset, uint32_ } // Call Original Method - if (!hide_chat_messages) { + if (!hide_chat_messages && !is_in_chat) { Gui_renderChatMessages(gui, y_offset, max_messages, disable_fading, font); } diff --git a/mods/src/sign/sign.cpp b/mods/src/sign/sign.cpp index ea1e0136..11cbac86 100644 --- a/mods/src/sign/sign.cpp +++ b/mods/src/sign/sign.cpp @@ -12,7 +12,13 @@ // Handle Backspace static int32_t sdl_key_to_minecraft_key_injection(int32_t sdl_key) { if (sdl_key == SDLK_BACKSPACE) { - return 8; + return 0x8; + } else if (sdl_key == SDLK_DELETE) { + return 0x2e; + } else if (sdl_key == SDLK_LEFT) { + return 0x25; + } else if (sdl_key == SDLK_RIGHT) { + return 0x27; } else { // Call Original Method return Common_sdl_key_to_minecraft_key(sdl_key); @@ -31,37 +37,17 @@ static void LocalPlayer_openTextEdit_injection(LocalPlayer *local_player, TileEn } // Store Text Input -std::vector input; void sign_key_press(char key) { - input.push_back(key); -} -static void clear_input(__attribute__((unused)) Minecraft *minecraft) { - input.clear(); -} - -// Handle Text Input -static void TextEditScreen_updateEvents_injection(TextEditScreen *screen) { - // Call Original Method - TextEditScreen_updateEvents_non_virtual(screen); - - if (!screen->passthrough_input) { - for (char key : input) { - // Handle Normal Key - screen->vtable->keyboardNewChar(screen, key); - } - } - clear_input(NULL); + Keyboard__inputText.push_back(key); } // Init void init_sign() { if (feature_has("Fix Sign Placement", server_disabled)) { - // Handle Backspace - overwrite_calls((void *) Common_sdl_key_to_minecraft_key, (void *) sdl_key_to_minecraft_key_injection); // Fix Signs patch_address(LocalPlayer_openTextEdit_vtable_addr, (void *) LocalPlayer_openTextEdit_injection); - patch_address(TextEditScreen_updateEvents_vtable_addr, (void *) TextEditScreen_updateEvents_injection); - // Clear Input On Input Tick - input_run_on_tick(clear_input); } + + // Handle Backspace + overwrite_calls((void *) Common_sdl_key_to_minecraft_key, (void *) sdl_key_to_minecraft_key_injection); } diff --git a/mods/src/text-input-box/README.md b/mods/src/text-input-box/README.md new file mode 100644 index 00000000..5132df38 --- /dev/null +++ b/mods/src/text-input-box/README.md @@ -0,0 +1,2 @@ +# ``text-input-box`` Mod +This mod implements a GUI component for text input. This is ported from [ReMinecraftPE](https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/client/gui/components/TextInputBox.cpp). diff --git a/mods/src/text-input-box/TextInputBox.cpp b/mods/src/text-input-box/TextInputBox.cpp new file mode 100644 index 00000000..cf98d3bd --- /dev/null +++ b/mods/src/text-input-box/TextInputBox.cpp @@ -0,0 +1,200 @@ +#include + +#include + +TextInputBox TextInputBox::create(int id, const std::string &placeholder, const std::string &text) { + // Construct + TextInputBox self; + GuiComponent_constructor(&self.super); + + // Setup + self.m_ID = id; + self.m_xPos = 0; + self.m_yPos = 0; + self.m_width = 0; + self.m_height = 0; + self.m_placeholder = placeholder; + self.m_text = text; + self.m_bFocused = false; + self.m_bEnabled = true; + self.m_bCursorOn = true; + self.m_insertHead = 0; + self.m_lastFlashed = 0; + self.m_pFont = nullptr; + self.m_maxLength = -1; + + // Return + return self; +} + +void TextInputBox::setSize(int x, int y, int width, int height) { + m_xPos = x; + m_yPos = y; + m_width = width; + m_height = height; +} + +void TextInputBox::init(Font *pFont) { + m_pFont = pFont; +} + +void TextInputBox::setEnabled(bool bEnabled) { + m_bEnabled = bEnabled; +} + +void TextInputBox::keyPressed(int key) { + if (!m_bFocused) { + return; + } + + switch (key) { + case 0x8: { + // Backspace + if (m_text.empty()) { + return; + } + if (m_insertHead <= 0) { + return; + } + if (m_insertHead > int(m_text.size())) { + m_insertHead = int(m_text.size()); + } + m_text.erase(m_text.begin() + m_insertHead - 1, m_text.begin() + m_insertHead); + m_insertHead--; + break; + } + case 0x2e: { + // Delete + if (m_text.empty()) { + return; + } + if (m_insertHead < 0) { + return; + } + if (m_insertHead >= int(m_text.size())) { + return; + } + m_text.erase(m_text.begin() + m_insertHead, m_text.begin() + m_insertHead + 1); + break; + } + case 0x25: { + // Left + m_insertHead--; + if (m_insertHead < 0) { + m_insertHead = 0; + } + break; + } + case 0x27: { + // Right + m_insertHead++; + if (!m_text.empty()) { + if (m_insertHead > int(m_text.size())) { + m_insertHead = int(m_text.size()); + } + } else { + m_insertHead = 0; + } + break; + } + case 0x0d: { + // Enter + m_bFocused = false; + break; + } + } +} + +void TextInputBox::tick() { + if (!m_lastFlashed) { + m_lastFlashed = Common_getTimeMs(); + } + + if (m_bFocused) { + if (Common_getTimeMs() > m_lastFlashed + 500) { + m_lastFlashed += 500; + m_bCursorOn ^= 1; + } + } else { + m_bCursorOn = false; + } +} + +void TextInputBox::setFocused(bool b) { + if (m_bFocused == b) { + return; + } + + m_bFocused = b; + if (b) { + m_lastFlashed = Common_getTimeMs(); + m_bCursorOn = true; + m_insertHead = int(m_text.size()); + } +} + +void TextInputBox::onClick(int x, int y) { + setFocused(clicked(x, y)); +} + +static int PADDING = 5; +void TextInputBox::charPressed(int k) { + if (!m_bFocused) { + return; + } + + // note: the width will increase by the same amount no matter where K is appended + std::string test_str = m_text + char(k); + if (m_maxLength != -1 && int(test_str.length()) > m_maxLength) { + return; + } + int width = Font_width(m_pFont, &test_str); + if (width < (m_width - PADDING)) { + m_text.insert(m_text.begin() + m_insertHead, k); + m_insertHead++; + } +} + +void TextInputBox::render() { + GuiComponent_fill(&super, m_xPos, m_yPos, m_xPos + m_width, m_yPos + m_height, 0xFFAAAAAA); + GuiComponent_fill(&super, m_xPos + 1, m_yPos + 1, m_xPos + m_width - 1, m_yPos + m_height - 1, 0xFF000000); + + int textYPos = (m_height - 8) / 2; + + if (m_text.empty()) { + GuiComponent_drawString(&super, m_pFont, &m_placeholder, m_xPos + PADDING, m_yPos + textYPos, 0x404040); + } else { + GuiComponent_drawString(&super, m_pFont, &m_text, m_xPos + PADDING, m_yPos + textYPos, 0xFFFFFF); + } + + if (m_bCursorOn) { + int xPos = 5; + + std::string substr = m_text.substr(0, m_insertHead); + xPos += Font_width(m_pFont, &substr); + + std::string str = "_"; + GuiComponent_drawString(&super, m_pFont, &str, m_xPos + xPos, m_yPos + textYPos + 2, 0xFFFFFF); + } +} + +bool TextInputBox::clicked(int xPos, int yPos) { + if (!m_bEnabled) { + return false; + } + + if (xPos < m_xPos) { + return false; + } + if (yPos < m_yPos) { + return false; + } + if (xPos >= m_xPos + m_width) { + return false; + } + if (yPos >= m_yPos + m_height) { + return false; + } + + return true; +} diff --git a/mods/src/text-input-box/TextInputScreen.cpp b/mods/src/text-input-box/TextInputScreen.cpp new file mode 100644 index 00000000..2b5a3b44 --- /dev/null +++ b/mods/src/text-input-box/TextInputScreen.cpp @@ -0,0 +1,40 @@ +#include + +#include + +// VTable +void TextInputScreen::setup(Screen_vtable *vtable) { + vtable->keyPressed = [](Screen *super2, int key) { + Screen_keyPressed_non_virtual(super2, key); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->keyPressed(key); + } + }; + vtable->keyboardNewChar = [](Screen *super2, char key) { + Screen_keyboardNewChar_non_virtual(super2, key); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->charPressed(key); + } + }; + vtable->mouseClicked = [](Screen *super2, int x, int y, int param_1) { + Screen_mouseClicked_non_virtual(super2, x, y, param_1); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->onClick(x, y); + } + }; + vtable->render = [](Screen *super2, int x, int y, float param_1) { + Screen_render_non_virtual(super2, x, y, param_1); + TextInputScreen *self = (TextInputScreen *) super2; + for (int i = 0; i < int(self->m_textInputs.size()); i++) { + TextInputBox *textInput = self->m_textInputs[i]; + textInput->tick(); + textInput->render(); + } + }; +} diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index d24b5aef..185b7433 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -106,7 +106,7 @@ set(SRC src/gui/components/OptionsPane.def src/gui/components/GuiComponent.def src/gui/components/Button.def - src/gui/components/Gui.def + src/gui/Gui.def src/gui/components/IntRectangle.def src/gui/components/RectangleArea.def src/gui/components/ScrollingPane.def @@ -145,6 +145,7 @@ set(SRC src/input/IBuildInput.def src/input/MouseBuildInput.def src/input/Mouse.def + src/input/Keyboard.def src/recipes/FurnaceRecipes.def src/recipes/Recipes.def src/recipes/Recipes_Type.def diff --git a/symbols/src/game/Minecraft.def b/symbols/src/game/Minecraft.def index b6440d88..637a92f7 100644 --- a/symbols/src/game/Minecraft.def +++ b/symbols/src/game/Minecraft.def @@ -40,6 +40,7 @@ property HitResult hit_result = 0xc38; property int progress = 0xc60; property PerfRenderer *perf_renderer = 0xcbc; property CommandServer *command_server = 0xcc0; +property Font *font = 0x16c; // Smooth Lighting static-property bool useAmbientOcclusion = 0x136b90; diff --git a/symbols/src/gui/Font.def b/symbols/src/gui/Font.def index e69de29b..2914ff8d 100644 --- a/symbols/src/gui/Font.def +++ b/symbols/src/gui/Font.def @@ -0,0 +1 @@ +method int width(std::string *string) = 0x24d4c; diff --git a/symbols/src/gui/components/Gui.def b/symbols/src/gui/Gui.def similarity index 100% rename from symbols/src/gui/components/Gui.def rename to symbols/src/gui/Gui.def diff --git a/symbols/src/gui/components/GuiComponent.def b/symbols/src/gui/components/GuiComponent.def index d2458062..77a775be 100644 --- a/symbols/src/gui/components/GuiComponent.def +++ b/symbols/src/gui/components/GuiComponent.def @@ -1,3 +1,8 @@ +size 0x8; + +constructor () = 0x28204; + method void blit(int x_dest, int y_dest, int x_src, int y_src, int width_dest, int height_dest, int width_src, int height_src) = 0x282a4; method void drawCenteredString(Font *font, std::string *text, int x, int y, int color) = 0x2821c; -method void fill(int x1, int y1, int x2, int y2, uint color) = 0x285f0; \ No newline at end of file +method void drawString(Font *font, std::string *text, int x, int y, int color) = 0x28284; +method void fill(int x1, int y1, int x2, int y2, uint color) = 0x285f0; diff --git a/symbols/src/gui/screens/Screen.def b/symbols/src/gui/screens/Screen.def index c02051d6..79c38187 100644 --- a/symbols/src/gui/screens/Screen.def +++ b/symbols/src/gui/screens/Screen.def @@ -1,15 +1,23 @@ extends GuiComponent; +size 0x48; +constructor () = 0x29028; + +vtable-size 0x74; vtable 0x1039d8; virtual-method void updateEvents() = 0x14; virtual-method void keyboardNewChar(char key) = 0x70; virtual-method void keyPressed(int key) = 0x6c; -virtual-method void render(int param_1, int param_2, float param_3) = 0x8; +virtual-method void render(int x, int y, float param_1) = 0x8; virtual-method bool handleBackEvent(bool param_1) = 0x24; virtual-method void tick() = 0x28; virtual-method void buttonClicked(Button *button) = 0x60; virtual-method void init() = 0xc; +virtual-method void mouseClicked(int x, int y, int param_1) = 0x64; +virtual-method void removed() = 0x2c; +virtual-method void renderBackground() = 0x30; +virtual-method void setupPositions() = 0x10; property Minecraft *minecraft = 0x14; property std::vector