Input Method Editor (#3634)
This commit is contained in:
parent
fd66ab60e3
commit
3232515a61
@ -3,7 +3,7 @@ include(FetchContent)
|
|||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
gtav_classes
|
gtav_classes
|
||||||
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
GIT_REPOSITORY https://github.com/Yimura/GTAV-Classes.git
|
||||||
GIT_TAG 21cbc2076b8d0ac9cbd98d05ebabadcced546f30
|
GIT_TAG b98cf8d4dafbde003bfbde27707574da77c01134
|
||||||
GIT_PROGRESS TRUE
|
GIT_PROGRESS TRUE
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
|
@ -22,6 +22,7 @@ class CGameScriptHandlerMgr;
|
|||||||
class CPedFactory;
|
class CPedFactory;
|
||||||
class GtaThread;
|
class GtaThread;
|
||||||
class GameDataHash;
|
class GameDataHash;
|
||||||
|
class InputMethodEditor;
|
||||||
|
|
||||||
namespace rage
|
namespace rage
|
||||||
{
|
{
|
||||||
@ -411,6 +412,9 @@ namespace big
|
|||||||
|
|
||||||
functions::is_ped_enemies_with m_is_ped_enemies_with;
|
functions::is_ped_enemies_with m_is_ped_enemies_with;
|
||||||
functions::can_do_damage_to_ped m_can_do_damage_to_ped;
|
functions::can_do_damage_to_ped m_can_do_damage_to_ped;
|
||||||
|
|
||||||
|
bool* m_allow_keyboard_layout_change;
|
||||||
|
InputMethodEditor* m_ime;
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned");
|
static_assert(sizeof(gta_pointers) % 8 == 0, "Pointers are not properly aligned");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "fiber_pool.hpp"
|
#include "fiber_pool.hpp"
|
||||||
#include "gui/components/components.hpp"
|
#include "gui/components/components.hpp"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
#include "util/input_method_editor.hpp"
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
{
|
{
|
||||||
@ -15,8 +16,19 @@ namespace big
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::IsItemActive())
|
if (ImGui::IsItemActive())
|
||||||
|
{
|
||||||
g.self.hud.typing = TYPING_TICKS;
|
g.self.hud.typing = TYPING_TICKS;
|
||||||
|
|
||||||
|
draw_input_method_editor();
|
||||||
|
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivated())
|
||||||
|
{
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +43,19 @@ namespace big
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::IsItemActive())
|
if (ImGui::IsItemActive())
|
||||||
|
{
|
||||||
g.self.hud.typing = TYPING_TICKS;
|
g.self.hud.typing = TYPING_TICKS;
|
||||||
|
|
||||||
|
draw_input_method_editor();
|
||||||
|
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivated())
|
||||||
|
{
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#include "fiber_pool.hpp"
|
#include "fiber_pool.hpp"
|
||||||
#include "gui/components/components.hpp"
|
#include "gui/components/components.hpp"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
#include "util/input_method_editor.hpp"
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
{
|
{
|
||||||
@ -11,7 +12,19 @@ namespace big
|
|||||||
g_fiber_pool->queue_job(std::move(cb));
|
g_fiber_pool->queue_job(std::move(cb));
|
||||||
|
|
||||||
if (ImGui::IsItemActive())
|
if (ImGui::IsItemActive())
|
||||||
|
{
|
||||||
g.self.hud.typing = TYPING_TICKS;
|
g.self.hud.typing = TYPING_TICKS;
|
||||||
|
|
||||||
|
draw_input_method_editor();
|
||||||
|
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivated())
|
||||||
|
{
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
|
||||||
|
}
|
||||||
|
|
||||||
return returned;
|
return returned;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +35,19 @@ namespace big
|
|||||||
g_fiber_pool->queue_job(std::move(cb));
|
g_fiber_pool->queue_job(std::move(cb));
|
||||||
|
|
||||||
if (ImGui::IsItemActive())
|
if (ImGui::IsItemActive())
|
||||||
|
{
|
||||||
g.self.hud.typing = TYPING_TICKS;
|
g.self.hud.typing = TYPING_TICKS;
|
||||||
|
|
||||||
|
draw_input_method_editor();
|
||||||
|
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivated())
|
||||||
|
{
|
||||||
|
*g_pointers->m_gta.m_allow_keyboard_layout_change = false;
|
||||||
|
}
|
||||||
|
|
||||||
return returned;
|
return returned;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
#include "hooking/hooking.hpp"
|
#include "hooking/hooking.hpp"
|
||||||
#include "renderer/renderer.hpp"
|
#include "renderer/renderer.hpp"
|
||||||
|
#include "util/input_method_editor.hpp"
|
||||||
|
|
||||||
namespace big
|
namespace big
|
||||||
{
|
{
|
||||||
@ -8,6 +9,11 @@ namespace big
|
|||||||
if (g_running) [[likely]]
|
if (g_running) [[likely]]
|
||||||
{
|
{
|
||||||
g_renderer.wndproc(hwnd, msg, wparam, lparam);
|
g_renderer.wndproc(hwnd, msg, wparam, lparam);
|
||||||
|
|
||||||
|
if (msg == WM_IME_COMPOSITION && lparam & GCS_RESULTSTR) [[unlikely]]
|
||||||
|
{
|
||||||
|
handle_ime_result();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam);
|
return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam);
|
||||||
|
@ -1958,6 +1958,16 @@ namespace big
|
|||||||
{
|
{
|
||||||
g_pointers->m_gta.m_can_do_damage_to_ped = ptr.add(1).rip().as<functions::can_do_damage_to_ped>();
|
g_pointers->m_gta.m_can_do_damage_to_ped = ptr.add(1).rip().as<functions::can_do_damage_to_ped>();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// Input Method Editor
|
||||||
|
{
|
||||||
|
"IME",
|
||||||
|
"75 25 89 44 24 28",
|
||||||
|
[](memory::handle ptr)
|
||||||
|
{
|
||||||
|
g_pointers->m_gta.m_allow_keyboard_layout_change = ptr.sub(4).rip().as<bool*>();
|
||||||
|
g_pointers->m_gta.m_ime = ptr.add(44).rip().sub(0x278 + 0x8).as<InputMethodEditor*>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
>(); // don't leave a trailing comma at the end
|
>(); // don't leave a trailing comma at the end
|
||||||
|
|
||||||
|
133
src/util/input_method_editor.hpp
Normal file
133
src/util/input_method_editor.hpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "pointers.hpp"
|
||||||
|
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
#include <misc/InputMethodEditor.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// https://github.com/ocornut/imgui/blob/864a2bf6b824f9c1329d8493386208d4b0fd311c/imgui_widgets.cpp#L3948
|
||||||
|
static void insert_into_input_text(ImGuiInputTextState* obj, const ImWchar* new_text, int new_text_len)
|
||||||
|
{
|
||||||
|
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
|
||||||
|
const int pos = obj->Stb.cursor;
|
||||||
|
const int text_len = obj->CurLenW;
|
||||||
|
|
||||||
|
const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);
|
||||||
|
if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Grow internal buffer if needed
|
||||||
|
if (new_text_len + text_len + 1 > obj->TextW.Size)
|
||||||
|
{
|
||||||
|
if (!is_resizable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImWchar* text = obj->TextW.Data;
|
||||||
|
if (pos != text_len)
|
||||||
|
memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));
|
||||||
|
memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));
|
||||||
|
|
||||||
|
obj->Edited = true;
|
||||||
|
obj->CurLenW += new_text_len;
|
||||||
|
obj->CurLenA += new_text_len_utf8;
|
||||||
|
obj->TextW[obj->CurLenW] = '\0';
|
||||||
|
|
||||||
|
obj->Stb.cursor += new_text_len;
|
||||||
|
obj->Stb.has_preferred_x = 0;
|
||||||
|
|
||||||
|
obj->CursorFollow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char16_to_char(char* out, const char16_t* in)
|
||||||
|
{
|
||||||
|
while (*in)
|
||||||
|
{
|
||||||
|
char16_t c = *in++;
|
||||||
|
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
*out++ = static_cast<char>(c);
|
||||||
|
}
|
||||||
|
else if (c < 0x800)
|
||||||
|
{
|
||||||
|
*out++ = static_cast<char>(0xC0 | (c >> 6));
|
||||||
|
*out++ = static_cast<char>(0x80 | (c & 0x3F));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out++ = static_cast<char>(0xE0 | (c >> 12));
|
||||||
|
*out++ = static_cast<char>(0x80 | ((c >> 6) & 0x3F));
|
||||||
|
*out++ = static_cast<char>(0x80 | (c & 0x3F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace big
|
||||||
|
{
|
||||||
|
inline void handle_ime_result()
|
||||||
|
{
|
||||||
|
auto state = ImGui::GetInputTextState(ImGui::GetActiveID());
|
||||||
|
|
||||||
|
if (!state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto context = ImmGetContext(g_pointers->m_hwnd);
|
||||||
|
|
||||||
|
ImWchar buf[31]{};
|
||||||
|
int len = ImmGetCompositionStringW(context, GCS_RESULTSTR, buf, sizeof(buf) - 1) / sizeof(ImWchar);
|
||||||
|
|
||||||
|
insert_into_input_text(state, buf, len);
|
||||||
|
|
||||||
|
ImmReleaseContext(g_pointers->m_hwnd, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void draw_input_method_editor()
|
||||||
|
{
|
||||||
|
if (!g_pointers->m_gta.m_ime->m_active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
constexpr size_t buf_size = ARRAYSIZE(InputMethodEditor::m_composition_string) * 3;
|
||||||
|
char buf[buf_size];
|
||||||
|
char16_to_char(buf, g_pointers->m_gta.m_ime->m_composition_string);
|
||||||
|
|
||||||
|
std::string text = buf;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < g_pointers->m_gta.m_ime->m_count; ++i)
|
||||||
|
{
|
||||||
|
char16_to_char(buf, g_pointers->m_gta.m_ime->m_candidate_list[i]);
|
||||||
|
|
||||||
|
text += '\n';
|
||||||
|
text += (i == g_pointers->m_gta.m_ime->m_selected_index ? '>' : ' ');
|
||||||
|
text += std::to_string(i + 1);
|
||||||
|
text += '\t';
|
||||||
|
text += buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float pd = 7.5f; // padding
|
||||||
|
constexpr float lt = 1.f; // line thickness
|
||||||
|
|
||||||
|
ImVec2 ts = ImGui::CalcTextSize(text.c_str());
|
||||||
|
|
||||||
|
ImVec2 bl = ImGui::GetItemRectMin(); // bottom-left
|
||||||
|
ImVec2 br = {bl.x + ts.x + 2 * pd, bl.y}; // bottom-right
|
||||||
|
ImVec2 tl = {bl.x, bl.y - ts.y - 2 * pd}; // top-left
|
||||||
|
ImVec2 tr = {br.x, tl.y}; // top-right
|
||||||
|
|
||||||
|
auto dl = ImGui::GetForegroundDrawList();
|
||||||
|
|
||||||
|
dl->AddRectFilled(tl, br, g.window.background_color | IM_COL32_A_MASK);
|
||||||
|
dl->AddText({tl.x + pd, tl.y + pd}, g.window.text_color, text.c_str());
|
||||||
|
|
||||||
|
dl->AddLine(tl, tr, IM_COL32_BLACK); // top
|
||||||
|
dl->AddLine({bl.x, bl.y - lt}, {br.x, br.y - lt}, IM_COL32_BLACK); // bottom
|
||||||
|
dl->AddLine(tl, bl, IM_COL32_BLACK); // left
|
||||||
|
dl->AddLine({tr.x - lt, tr.y}, {br.x - lt, br.y}, IM_COL32_BLACK); // right
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user