In-Game Chat
This commit is contained in:
parent
d175f692e0
commit
58713976d4
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
35
mods/include/mods/text-input-box/TextInputBox.h
Normal file
35
mods/include/mods/text-input-box/TextInputBox.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
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 = "");
|
||||
};
|
12
mods/include/mods/text-input-box/TextInputScreen.h
Normal file
12
mods/include/mods/text-input-box/TextInputScreen.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
#include <mods/text-input-box/TextInputBox.h>
|
||||
|
||||
struct TextInputScreen {
|
||||
Screen super;
|
||||
std::vector<TextInputBox *> m_textInputs;
|
||||
|
||||
static void setup(Screen_vtable *vtable);
|
||||
};
|
@ -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
|
||||
|
@ -5,9 +5,6 @@
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
@ -22,9 +19,6 @@
|
||||
#include "chat-internal.h"
|
||||
#include <mods/chat/chat.h>
|
||||
|
||||
// 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<std::string> 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
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +0,0 @@
|
||||
// Config Needs To Load First
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
// Chat UI Code Is Useless In Headless Mode
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <media-layer/core.h>
|
||||
|
||||
#include "chat-internal.h"
|
||||
#include <mods/chat/chat.h>
|
||||
|
||||
// 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
|
98
mods/src/chat/ui.cpp
Normal file
98
mods/src/chat/ui.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
// Config Needs To Load First
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
// Chat UI Code Is Useless In Headless Mode
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
|
||||
#include "chat-internal.h"
|
||||
#include <mods/chat/chat.h>
|
||||
#include <mods/text-input-box/TextInputScreen.h>
|
||||
#include <mods/misc/misc.h>
|
||||
|
||||
// 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
|
@ -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
|
||||
handled = 1;
|
||||
} else if (event->key.keysym.sym == SDLK_ESCAPE) {
|
||||
// Treat Escape As Back Button Press (This Fixes Issues With Signs)
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<char> 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);
|
||||
}
|
||||
|
2
mods/src/text-input-box/README.md
Normal file
2
mods/src/text-input-box/README.md
Normal file
@ -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).
|
200
mods/src/text-input-box/TextInputBox.cpp
Normal file
200
mods/src/text-input-box/TextInputBox.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include <mods/text-input-box/TextInputBox.h>
|
||||
|
||||
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;
|
||||
}
|
40
mods/src/text-input-box/TextInputScreen.cpp
Normal file
40
mods/src/text-input-box/TextInputScreen.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include <mods/text-input-box/TextInputScreen.h>
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1 @@
|
||||
method int width(std::string *string) = 0x24d4c;
|
@ -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 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;
|
@ -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<Button *> rendered_buttons = 0x18;
|
||||
@ -17,3 +25,4 @@ property std::vector<Button *> selectable_buttons = 0x30;
|
||||
property int width = 0x8;
|
||||
property int height = 0xc;
|
||||
property bool passthrough_input = 0x10;
|
||||
property Font *font = 0x40;
|
||||
|
@ -1,5 +1,6 @@
|
||||
extends Screen;
|
||||
|
||||
size 0x16c;
|
||||
|
||||
constructor () = 0x3afbc;
|
||||
|
||||
vtable 0x1053c0;
|
||||
|
1
symbols/src/input/Keyboard.def
Normal file
1
symbols/src/input/Keyboard.def
Normal file
@ -0,0 +1 @@
|
||||
static-property std::vector<char> _inputText = 0x1364f0;
|
Loading…
Reference in New Issue
Block a user