mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-12 04:35:56 +00:00
Merge 54621e6099
into 80f7ec2681
This commit is contained in:
commit
d065876675
10 changed files with 1443 additions and 51 deletions
|
@ -510,6 +510,10 @@ set(IME_LIB src/core/libraries/ime/error_dialog.cpp
|
||||||
src/core/libraries/ime/ime_dialog.h
|
src/core/libraries/ime/ime_dialog.h
|
||||||
src/core/libraries/ime/ime_ui.cpp
|
src/core/libraries/ime/ime_ui.cpp
|
||||||
src/core/libraries/ime/ime_ui.h
|
src/core/libraries/ime/ime_ui.h
|
||||||
|
src/core/libraries/ime/ime_keyboard_layouts.cpp
|
||||||
|
src/core/libraries/ime/ime_keyboard_layouts.h
|
||||||
|
src/core/libraries/ime/ime_keyboard_ui.cpp
|
||||||
|
src/core/libraries/ime/ime_keyboard_ui.h
|
||||||
src/core/libraries/ime/ime.cpp
|
src/core/libraries/ime/ime.cpp
|
||||||
src/core/libraries/ime/ime.h
|
src/core/libraries/ime/ime.h
|
||||||
src/core/libraries/ime/ime_error.h
|
src/core/libraries/ime/ime_error.h
|
||||||
|
|
|
@ -148,7 +148,91 @@ OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() {
|
||||||
return g_ime_dlg_status;
|
return g_ime_dlg_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static std::string ConvertUtf16ToUtf8(const char16_t* src) {
|
||||||
|
if (!src) {
|
||||||
|
return "(null)";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
while (*src) {
|
||||||
|
char16_t c = *src++;
|
||||||
|
|
||||||
|
if (c < 0x80) {
|
||||||
|
result += static_cast<char>(c);
|
||||||
|
} else if (c < 0x800) {
|
||||||
|
result += static_cast<char>(0xC0 | (c >> 6));
|
||||||
|
result += static_cast<char>(0x80 | (c & 0x3F));
|
||||||
|
} else {
|
||||||
|
result += static_cast<char>(0xE0 | (c >> 12));
|
||||||
|
result += static_cast<char>(0x80 | ((c >> 6) & 0x3F));
|
||||||
|
result += static_cast<char>(0x80 | (c & 0x3F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpImeDialogParam(const Libraries::ImeDialog::OrbisImeDialogParam* param,
|
||||||
|
const Libraries::ImeDialog::OrbisImeParamExtended* ext_param) {
|
||||||
|
if (!param) {
|
||||||
|
LOG_INFO(Lib_ImeDialog, "OpenImeDialog called with null param.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTF-16 to UTF-8 conversion using your safe method
|
||||||
|
std::string title_utf8 = "(null)";
|
||||||
|
std::string placeholder_utf8 = "(null)";
|
||||||
|
title_utf8 = ConvertUtf16ToUtf8(param->title);
|
||||||
|
placeholder_utf8 = ConvertUtf16ToUtf8(param->placeholder);
|
||||||
|
|
||||||
|
LOG_INFO(
|
||||||
|
Lib_ImeDialog,
|
||||||
|
"OpenImeDialog:\n"
|
||||||
|
" user_id={}, type={}, option=0x{:X}, max_text_length={}, supported_languages=0x{:X}\n"
|
||||||
|
" title=\"{}\", placeholder=\"{}\", input_text_buffer={}",
|
||||||
|
param->user_id, static_cast<int>(param->type), static_cast<u32>(param->option),
|
||||||
|
param->max_text_length, param->supported_languages, title_utf8, placeholder_utf8,
|
||||||
|
param->input_text_buffer ? reinterpret_cast<uintptr_t>(param->input_text_buffer) : 0);
|
||||||
|
|
||||||
|
if (ext_param) {
|
||||||
|
LOG_INFO(Lib_ImeDialog,
|
||||||
|
"ExtendedParam:\n"
|
||||||
|
" color_base=({}, {}, {}, {}) color_line=({}, {}, {}, {}) color_text_field=({}, "
|
||||||
|
"{}, {}, {})\n"
|
||||||
|
" color_preedit=({}, {}, {}, {}) color_button_default=({}, {}, {}, {}) "
|
||||||
|
"color_button_function=({}, {}, {}, {})\n"
|
||||||
|
" color_button_symbol=({}, {}, {}, {}) color_text=({}, {}, {}, {}) "
|
||||||
|
"color_special=({}, {}, {}, {})\n"
|
||||||
|
" priority={}",
|
||||||
|
ext_param->color_base.r, ext_param->color_base.g, ext_param->color_base.b,
|
||||||
|
ext_param->color_base.a, ext_param->color_line.r, ext_param->color_line.g,
|
||||||
|
ext_param->color_line.b, ext_param->color_line.a, ext_param->color_text_field.r,
|
||||||
|
ext_param->color_text_field.g, ext_param->color_text_field.b,
|
||||||
|
ext_param->color_text_field.a, ext_param->color_preedit.r,
|
||||||
|
ext_param->color_preedit.g, ext_param->color_preedit.b, ext_param->color_preedit.a,
|
||||||
|
ext_param->color_button_default.r, ext_param->color_button_default.g,
|
||||||
|
ext_param->color_button_default.b, ext_param->color_button_default.a,
|
||||||
|
ext_param->color_button_function.r, ext_param->color_button_function.g,
|
||||||
|
ext_param->color_button_function.b, ext_param->color_button_function.a,
|
||||||
|
ext_param->color_button_symbol.r, ext_param->color_button_symbol.g,
|
||||||
|
ext_param->color_button_symbol.b, ext_param->color_button_symbol.a,
|
||||||
|
ext_param->color_text.r, ext_param->color_text.g, ext_param->color_text.b,
|
||||||
|
ext_param->color_text.a, ext_param->color_special.r, ext_param->color_special.g,
|
||||||
|
ext_param->color_special.b, ext_param->color_special.a,
|
||||||
|
static_cast<int>(ext_param->priority));
|
||||||
|
} else {
|
||||||
|
LOG_INFO(Lib_ImeDialog, "ExtendedParam: (none)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
|
Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) {
|
||||||
|
|
||||||
|
DumpImeDialogParam(param, extended);
|
||||||
|
|
||||||
if (g_ime_dlg_status != OrbisImeDialogStatus::None) {
|
if (g_ime_dlg_status != OrbisImeDialogStatus::None) {
|
||||||
LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
|
LOG_INFO(Lib_ImeDialog, "IME dialog is already running");
|
||||||
return Error::BUSY;
|
return Error::BUSY;
|
||||||
|
@ -221,7 +305,7 @@ Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExt
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) {
|
if (param->max_text_length > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) {
|
||||||
LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength");
|
LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength ({})", param->max_text_length);
|
||||||
return Error::INVALID_MAX_TEXT_LENGTH;
|
return Error::INVALID_MAX_TEXT_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,18 +13,48 @@
|
||||||
#include "core/tls.h"
|
#include "core/tls.h"
|
||||||
#include "imgui/imgui_std.h"
|
#include "imgui/imgui_std.h"
|
||||||
|
|
||||||
|
#include "core/libraries/ime/ime_keyboard_layouts.h" // c16rtomb, layout tables
|
||||||
|
#include "core/libraries/ime/ime_keyboard_ui.h" // DrawVirtualKeyboard, Utf8SafeBackspace
|
||||||
|
|
||||||
using namespace ImGui;
|
using namespace ImGui;
|
||||||
|
|
||||||
|
/* small helper for the OK/Cancel buttons */
|
||||||
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
|
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
|
||||||
|
|
||||||
|
/* convert palette colour from Orbis struct to ImGui format */
|
||||||
|
static ImU32 ConvertColor(const Libraries::ImeDialog::OrbisImeColor& c) {
|
||||||
|
return IM_COL32(c.r, c.g, c.b, c.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* Libraries::ImeDialog implementation
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
namespace Libraries::ImeDialog {
|
namespace Libraries::ImeDialog {
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------
|
||||||
|
* class‑static pointer – single definition
|
||||||
|
* ----------------------------------------------------------*/
|
||||||
|
ImeDialogUi* ImeDialogUi::g_activeImeDialogUi = nullptr;
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------
|
||||||
|
* keyboard‑to‑dialog event bridge
|
||||||
|
* ----------------------------------------------------------*/
|
||||||
|
static void KeyboardCallbackBridge(const VirtualKeyEvent* evt) {
|
||||||
|
if (ImeDialogUi::g_activeImeDialogUi && evt)
|
||||||
|
ImeDialogUi::g_activeImeDialogUi->OnVirtualKeyEvent(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* ImeDialogState : constructors, helpers
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
||||||
const OrbisImeParamExtended* extended) {
|
const OrbisImeParamExtended* extended)
|
||||||
|
: extended_param_(extended) {
|
||||||
if (!param) {
|
if (!param) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* basic param copy */
|
||||||
user_id = param->user_id;
|
user_id = param->user_id;
|
||||||
is_multi_line = True(param->option & OrbisImeDialogOption::Multiline);
|
is_multi_line = True(param->option & OrbisImeDialogOption::Multiline);
|
||||||
is_numeric = param->type == OrbisImeType::Number;
|
is_numeric = param->type == OrbisImeType::Number;
|
||||||
|
@ -35,6 +65,7 @@ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
|
||||||
max_text_length = param->max_text_length;
|
max_text_length = param->max_text_length;
|
||||||
text_buffer = param->input_text_buffer;
|
text_buffer = param->input_text_buffer;
|
||||||
|
|
||||||
|
/* UTF‑16 → UTF‑8 conversions */
|
||||||
if (param->title) {
|
if (param->title) {
|
||||||
std::size_t title_len = std::char_traits<char16_t>::length(param->title);
|
std::size_t title_len = std::char_traits<char16_t>::length(param->title);
|
||||||
title.resize(title_len * 4 + 1);
|
title.resize(title_len * 4 + 1);
|
||||||
|
@ -166,12 +197,44 @@ bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* ImeDialogUi : constructor / destructor / move
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status,
|
ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status,
|
||||||
OrbisImeDialogResult* result)
|
OrbisImeDialogResult* result)
|
||||||
: state(state), status(status), result(result) {
|
: state(state), status(status), result(result) {
|
||||||
|
const OrbisImeParamExtended* incoming = state ? state->GetExtendedParam() : nullptr;
|
||||||
|
if (incoming) {
|
||||||
|
// copy caller’s palette
|
||||||
|
ext_ = *incoming;
|
||||||
|
} else {
|
||||||
|
// zero-init and then overwrite the color fields you need
|
||||||
|
std::memset(&ext_, 0, sizeof(ext_));
|
||||||
|
ext_.color_base = {19, 19, 21, 240};
|
||||||
|
ext_.color_line = {255, 255, 255, 255};
|
||||||
|
ext_.color_text_field = {26, 26, 28, 240};
|
||||||
|
ext_.color_preedit = {0, 0, 0, 255};
|
||||||
|
ext_.color_button_default = {45, 45, 45, 255};
|
||||||
|
ext_.color_button_function = {72, 72, 74, 255};
|
||||||
|
ext_.color_button_symbol = {96, 96, 98, 255};
|
||||||
|
ext_.color_text = {255, 255, 255, 255};
|
||||||
|
ext_.color_special = {0, 123, 200, 255};
|
||||||
|
ext_.priority = OrbisImePanelPriority::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For text, lines, etc.
|
||||||
|
kb_style.color_text = ConvertColor(ext_.color_text);
|
||||||
|
kb_style.color_line = ConvertColor(ext_.color_line);
|
||||||
|
|
||||||
|
// Button colors
|
||||||
|
kb_style.color_button_default = ConvertColor(ext_.color_button_default);
|
||||||
|
kb_style.color_button_symbol = ConvertColor(ext_.color_button_symbol);
|
||||||
|
kb_style.color_button_function = ConvertColor(ext_.color_button_function);
|
||||||
|
kb_style.color_special = ConvertColor(ext_.color_special);
|
||||||
|
|
||||||
if (state && *status == OrbisImeDialogStatus::Running) {
|
if (state && *status == OrbisImeDialogStatus::Running) {
|
||||||
AddLayer(this);
|
AddLayer(this);
|
||||||
|
ImeDialogUi::g_activeImeDialogUi = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +242,10 @@ ImeDialogUi::~ImeDialogUi() {
|
||||||
std::scoped_lock lock(draw_mutex);
|
std::scoped_lock lock(draw_mutex);
|
||||||
|
|
||||||
Free();
|
Free();
|
||||||
|
|
||||||
|
if (ImeDialogUi::g_activeImeDialogUi == this) {
|
||||||
|
ImeDialogUi::g_activeImeDialogUi = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
|
ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
|
||||||
|
@ -192,6 +259,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept
|
||||||
|
|
||||||
if (state && *status == OrbisImeDialogStatus::Running) {
|
if (state && *status == OrbisImeDialogStatus::Running) {
|
||||||
AddLayer(this);
|
AddLayer(this);
|
||||||
|
ImeDialogUi::g_activeImeDialogUi = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +277,7 @@ ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) {
|
||||||
|
|
||||||
if (state && *status == OrbisImeDialogStatus::Running) {
|
if (state && *status == OrbisImeDialogStatus::Running) {
|
||||||
AddLayer(this);
|
AddLayer(this);
|
||||||
|
ImeDialogUi::g_activeImeDialogUi = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -218,6 +287,9 @@ void ImeDialogUi::Free() {
|
||||||
RemoveLayer(this);
|
RemoveLayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* ImeDialogUi : main ImGui draw routine
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
void ImeDialogUi::Draw() {
|
void ImeDialogUi::Draw() {
|
||||||
std::unique_lock lock{draw_mutex};
|
std::unique_lock lock{draw_mutex};
|
||||||
|
|
||||||
|
@ -228,16 +300,16 @@ void ImeDialogUi::Draw() {
|
||||||
if (!status || *status != OrbisImeDialogStatus::Running) {
|
if (!status || *status != OrbisImeDialogStatus::Running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, ConvertColor(ext_.color_base));
|
||||||
const auto& ctx = *GetCurrentContext();
|
const auto& ctx = *GetCurrentContext();
|
||||||
const auto& io = ctx.IO;
|
const auto& io = ctx.IO;
|
||||||
|
|
||||||
ImVec2 window_size;
|
ImVec2 window_size;
|
||||||
|
|
||||||
if (state->is_multi_line) {
|
if (state->is_multi_line) {
|
||||||
window_size = {500.0f, 300.0f};
|
window_size = {500.0f, 500.0f};
|
||||||
} else {
|
} else {
|
||||||
window_size = {500.0f, 150.0f};
|
window_size = {500.0f, 350.0f};
|
||||||
}
|
}
|
||||||
|
|
||||||
CentralizeNextWindow();
|
CentralizeNextWindow();
|
||||||
|
@ -250,79 +322,95 @@ void ImeDialogUi::Draw() {
|
||||||
|
|
||||||
if (Begin("IME Dialog##ImeDialog", nullptr,
|
if (Begin("IME Dialog##ImeDialog", nullptr,
|
||||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
|
||||||
DrawPrettyBackground();
|
// DrawPrettyBackground();
|
||||||
|
|
||||||
if (!state->title.empty()) {
|
/* ---------- title ---------- */
|
||||||
SetWindowFontScale(1.7f);
|
DrawTitle();
|
||||||
TextUnformatted(state->title.data());
|
|
||||||
SetWindowFontScale(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* ---------- input box ---------- */
|
||||||
if (state->is_multi_line) {
|
if (state->is_multi_line) {
|
||||||
DrawMultiLineInputText();
|
DrawMultiLineInputText();
|
||||||
} else {
|
} else {
|
||||||
DrawInputText();
|
DrawInputText();
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCursorPosY(GetCursorPosY() + 10.0f);
|
/* ---------- dummy prediction bar with Cancel button ---------- */
|
||||||
|
DrawPredictionBarAnCancelButton();
|
||||||
|
|
||||||
const char* button_text;
|
/* ---------- on‑screen keyboard ---------- */
|
||||||
|
DrawVirtualKeyboardSection();
|
||||||
|
|
||||||
switch (state->enter_label) {
|
/* ---------- OK / Cancel buttons ---------- */
|
||||||
case OrbisImeEnterLabel::Go:
|
/*
|
||||||
button_text = "Go##ImeDialogOK";
|
DrawOkAndCancelButtons();
|
||||||
break;
|
*/
|
||||||
case OrbisImeEnterLabel::Search:
|
|
||||||
button_text = "Search##ImeDialogOK";
|
|
||||||
break;
|
|
||||||
case OrbisImeEnterLabel::Send:
|
|
||||||
button_text = "Send##ImeDialogOK";
|
|
||||||
break;
|
|
||||||
case OrbisImeEnterLabel::Default:
|
|
||||||
default:
|
|
||||||
button_text = "OK##ImeDialogOK";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
float button_spacing = 10.0f;
|
|
||||||
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
|
|
||||||
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
|
|
||||||
|
|
||||||
SetCursorPosX(button_start_pos);
|
|
||||||
|
|
||||||
if (Button(button_text, BUTTON_SIZE) ||
|
|
||||||
(!state->is_multi_line && IsKeyPressed(ImGuiKey_Enter))) {
|
|
||||||
*status = OrbisImeDialogStatus::Finished;
|
|
||||||
result->endstatus = OrbisImeDialogEndStatus::Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
SameLine(0.0f, button_spacing);
|
|
||||||
|
|
||||||
if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) {
|
|
||||||
*status = OrbisImeDialogStatus::Finished;
|
|
||||||
result->endstatus = OrbisImeDialogEndStatus::UserCanceled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
End();
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
first_render = false;
|
first_render = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* helper draw functions (unchanged)
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
void ImeDialogUi::DrawInputText() {
|
void ImeDialogUi::DrawInputText() {
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
// ─── Apply ext_ colors ───────────────────────────────────────────────────
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_NavHighlight, ConvertColor(ext_.color_line));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColor(ext_.color_text_field)); // background
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColor(ext_.color_text_field));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColor(ext_.color_text_field));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Border, ConvertColor(ext_.color_line)); // border line
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColor(ext_.color_text)); // typed text
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
|
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
|
||||||
SetCursorPosX(20.0f);
|
SetCursorPosX(20.0f);
|
||||||
if (first_render) {
|
if (first_render) {
|
||||||
SetKeyboardFocusHere();
|
SetKeyboardFocusHere();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
||||||
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
||||||
state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter,
|
state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter,
|
||||||
InputTextCallback, this)) {
|
InputTextCallback, this)) {
|
||||||
state->input_changed = true;
|
state->input_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CARET: manually render even if not focused
|
||||||
|
if (!ImGui::IsItemActive()) {
|
||||||
|
// Calculate input field position
|
||||||
|
ImVec2 input_pos = ImGui::GetItemRectMin();
|
||||||
|
|
||||||
|
// Find where to draw the caret
|
||||||
|
DrawCaretForInputText(state->current_text.begin(), state->caret_index, input_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────── replicate keyboard’s hover→nav focus highlight ──────
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetItemCurrentNavFocus();
|
||||||
|
ImGui::KeepNavHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────── pop ALL style colors (5 for input + 1 for NavHighlight) ──────
|
||||||
|
ImGui::PopStyleColor(6);
|
||||||
|
ImGui::EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImeDialogUi::DrawMultiLineInputText() {
|
void ImeDialogUi::DrawMultiLineInputText() {
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
// ─── Apply the same ext_ colors ───────────────────────────────────────────
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_NavHighlight, ConvertColor(ext_.color_line));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ConvertColor(ext_.color_text_field));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ConvertColor(ext_.color_text_field));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ConvertColor(ext_.color_text_field));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Border, ConvertColor(ext_.color_line));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColor(ext_.color_text));
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────
|
||||||
ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f};
|
ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f};
|
||||||
SetCursorPosX(20.0f);
|
SetCursorPosX(20.0f);
|
||||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter |
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter |
|
||||||
|
@ -331,10 +419,18 @@ void ImeDialogUi::DrawMultiLineInputText() {
|
||||||
SetKeyboardFocusHere();
|
SetKeyboardFocusHere();
|
||||||
}
|
}
|
||||||
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data();
|
||||||
|
|
||||||
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(),
|
||||||
state->max_text_length, input_size, flags, InputTextCallback, this)) {
|
state->max_text_length, input_size, flags, InputTextCallback, this)) {
|
||||||
state->input_changed = true;
|
state->input_changed = true;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetItemCurrentNavFocus();
|
||||||
|
ImGui::KeepNavHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(6);
|
||||||
|
ImGui::EndGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||||
|
@ -359,8 +455,8 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||||
.keycode = 0,
|
.keycode = 0,
|
||||||
.character = 0,
|
.character = 0,
|
||||||
.status = 1, // ??? 1 = key pressed, 0 = key released
|
.status = 1, // ??? 1 = key pressed, 0 = key released
|
||||||
.type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use
|
.type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe
|
||||||
// the current language?)
|
// use the current language?)
|
||||||
.user_id = ui->state->user_id,
|
.user_id = ui->state->user_id,
|
||||||
.resource_id = 0,
|
.resource_id = 0,
|
||||||
.timestamp = 0,
|
.timestamp = 0,
|
||||||
|
@ -382,4 +478,193 @@ int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImeDialogUi::DrawTitle() {
|
||||||
|
if (!state->title.empty()) {
|
||||||
|
SetCursorPosX(20.0f);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColor(ext_.color_text));
|
||||||
|
SetWindowFontScale(1.7f);
|
||||||
|
TextUnformatted(state->title.data());
|
||||||
|
SetWindowFontScale(1.0f);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImeDialogUi::DrawOkAndCancelButtons() {
|
||||||
|
SetCursorPosY(GetCursorPosY() + 10.0f);
|
||||||
|
|
||||||
|
const char* button_text;
|
||||||
|
|
||||||
|
switch (state->enter_label) {
|
||||||
|
case OrbisImeEnterLabel::Go:
|
||||||
|
button_text = "Go##ImeDialogOK";
|
||||||
|
break;
|
||||||
|
case OrbisImeEnterLabel::Search:
|
||||||
|
button_text = "Search##ImeDialogOK";
|
||||||
|
break;
|
||||||
|
case OrbisImeEnterLabel::Send:
|
||||||
|
button_text = "Send##ImeDialogOK";
|
||||||
|
break;
|
||||||
|
case OrbisImeEnterLabel::Default:
|
||||||
|
default:
|
||||||
|
button_text = "OK##ImeDialogOK";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float button_spacing = 10.0f;
|
||||||
|
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
|
||||||
|
float button_start_pos = (GetWindowWidth() - total_button_width) / 2.0f;
|
||||||
|
|
||||||
|
SetCursorPosX(button_start_pos);
|
||||||
|
|
||||||
|
if (Button(button_text, BUTTON_SIZE) ||
|
||||||
|
(!state->is_multi_line && IsKeyPressed(ImGuiKey_Enter))) {
|
||||||
|
*status = OrbisImeDialogStatus::Finished;
|
||||||
|
result->endstatus = OrbisImeDialogEndStatus::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
SameLine(0.0f, button_spacing);
|
||||||
|
|
||||||
|
if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) {
|
||||||
|
*status = OrbisImeDialogStatus::Finished;
|
||||||
|
result->endstatus = OrbisImeDialogEndStatus::UserCanceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw keyboard in a sub‑ID scope */
|
||||||
|
void ImeDialogUi::DrawVirtualKeyboardSection() {
|
||||||
|
ImGui::PushID("VirtualKeyboardSection");
|
||||||
|
DrawVirtualKeyboard(kb_mode, state->type, shift_state, kb_language, KeyboardCallbackBridge,
|
||||||
|
kb_style);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImeDialogUi::DrawPredictionBarAnCancelButton() {
|
||||||
|
const float pad = 5.0f;
|
||||||
|
const float width = kb_style.layout_width;
|
||||||
|
const float bar_h = 25.0f;
|
||||||
|
SetCursorPosY(GetCursorPosY() + 5.0f);
|
||||||
|
SetCursorPosX(0.0f);
|
||||||
|
ImVec2 p0 = GetCursorScreenPos();
|
||||||
|
ImVec2 p1 = ImVec2(p0.x + width - bar_h - 2 * pad, p0.y + bar_h);
|
||||||
|
// GetWindowDrawList()->AddRectFilled(p0, p1, IM_COL32(0, 0, 0, 255));
|
||||||
|
|
||||||
|
/* label */
|
||||||
|
// ImGui::SetCursorScreenPos(ImVec2(p0.x, p0.y));
|
||||||
|
// ImGui::PushStyleColor(ImGuiCol_Text, kb_style.color_text);
|
||||||
|
// Selectable("dummy prediction", false, 0, ImVec2(width - bar_h, bar_h));
|
||||||
|
// ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
SetCursorPosX(pad);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_NavHighlight, ConvertColor(ext_.color_line));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ConvertColor(ext_.color_preedit));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColor(ext_.color_preedit));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ConvertColor(ext_.color_preedit));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ConvertColor(ext_.color_text));
|
||||||
|
|
||||||
|
if (ImGui::Button("predict", ImVec2(width - bar_h - 3 * pad, bar_h))) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetItemCurrentNavFocus();
|
||||||
|
ImGui::KeepNavHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(4);
|
||||||
|
|
||||||
|
/* X button */
|
||||||
|
// ImGui::SameLine(width - bar_h);
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(p0.x + width - bar_h - pad, p0.y));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, kb_style.color_button_function);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kb_style.color_button_function);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, kb_style.color_button_function);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, kb_style.color_text);
|
||||||
|
|
||||||
|
if (ImGui::Button("╳", ImVec2(bar_h, bar_h))) {
|
||||||
|
*status = OrbisImeDialogStatus::Finished;
|
||||||
|
result->endstatus = OrbisImeDialogEndStatus::UserCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetItemCurrentNavFocus();
|
||||||
|
ImGui::KeepNavHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopStyleColor(5);
|
||||||
|
|
||||||
|
SetCursorPosX(0.0f);
|
||||||
|
SetCursorPosY(GetCursorPosY() + 5.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*─────────────────────────────────────────────────────────────*
|
||||||
|
* helper draw functions (new)
|
||||||
|
*─────────────────────────────────────────────────────────────*/
|
||||||
|
void ImeDialogUi::OnVirtualKeyEvent(const VirtualKeyEvent* evt) {
|
||||||
|
if (!evt || !state || !evt->key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const KeyEntry* key = evt->key;
|
||||||
|
|
||||||
|
/* Treat Repeat exactly like Down */
|
||||||
|
if (evt->type == VirtualKeyEventType::Down || evt->type == VirtualKeyEventType::Repeat) {
|
||||||
|
switch (key->type) {
|
||||||
|
case KeyType::Character: {
|
||||||
|
char utf8[8]{};
|
||||||
|
int n = c16rtomb(utf8, key->character);
|
||||||
|
if (n > 0) {
|
||||||
|
state->InsertUtf8AtCaret(utf8, (size_t)n);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KeyType::Function:
|
||||||
|
switch (key->keycode) {
|
||||||
|
case KC_LEFT: // Your custom code for ◀ button
|
||||||
|
if (state->caret_index > 0)
|
||||||
|
state->caret_index--;
|
||||||
|
LOG_INFO(Lib_ImeDialog, "Caret index = {}", state->caret_index);
|
||||||
|
break;
|
||||||
|
case KC_RIGHT: // Your custom code for ▶ button
|
||||||
|
if (state->caret_index < (int)state->current_text.size())
|
||||||
|
state->caret_index++;
|
||||||
|
LOG_INFO(Lib_ImeDialog, "Caret index = {}", state->caret_index);
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
state->BackspaceUtf8AtCaret();
|
||||||
|
break; // Backspace
|
||||||
|
case 0x0D:
|
||||||
|
*status = OrbisImeDialogStatus::Finished; // Enter
|
||||||
|
result->endstatus = OrbisImeDialogEndStatus::Ok;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KC_SYM1:
|
||||||
|
kb_mode = KeyboardMode::Symbols1;
|
||||||
|
break;
|
||||||
|
case KC_SYM2:
|
||||||
|
kb_mode = KeyboardMode::Symbols2;
|
||||||
|
break;
|
||||||
|
case KC_ACCENTS:
|
||||||
|
kb_mode = KeyboardMode::AccentLetters;
|
||||||
|
break;
|
||||||
|
case KC_LETTERS:
|
||||||
|
kb_mode = KeyboardMode::Letters;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x10: // Shift / Caps
|
||||||
|
case 0x105:
|
||||||
|
shift_state = (shift_state == ShiftState::None)
|
||||||
|
? ShiftState::Shift
|
||||||
|
: (shift_state == ShiftState::Shift ? ShiftState::CapsLock
|
||||||
|
: ShiftState::None);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Up is available if you need it later; currently ignored */
|
||||||
|
}
|
||||||
} // namespace Libraries::ImeDialog
|
} // namespace Libraries::ImeDialog
|
||||||
|
|
|
@ -3,21 +3,28 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring> // for strncpy / memcpy
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include "common/cstring.h"
|
#include "common/cstring.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/ime/ime_dialog.h"
|
#include "core/libraries/ime/ime_dialog.h"
|
||||||
|
#include "core/libraries/ime/ime_keyboard_ui.h"
|
||||||
#include "imgui/imgui_layer.h"
|
#include "imgui/imgui_layer.h"
|
||||||
|
|
||||||
namespace Libraries::ImeDialog {
|
namespace Libraries::ImeDialog {
|
||||||
|
|
||||||
|
// Forward declaration so we can befriend it
|
||||||
class ImeDialogUi;
|
class ImeDialogUi;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// ImeDialogState — holds the text and options for the IME dialog
|
||||||
|
//---------------------------------------------------------------------
|
||||||
class ImeDialogState final {
|
class ImeDialogState final {
|
||||||
friend ImeDialogUi;
|
friend class ImeDialogUi; // full access for the dialog‑UI layer
|
||||||
|
|
||||||
|
/*────────────────────────── private data ─────────────────────────*/
|
||||||
bool input_changed = false;
|
bool input_changed = false;
|
||||||
|
|
||||||
s32 user_id{};
|
s32 user_id{};
|
||||||
|
@ -31,11 +38,17 @@ class ImeDialogState final {
|
||||||
char16_t* text_buffer{};
|
char16_t* text_buffer{};
|
||||||
std::vector<char> title;
|
std::vector<char> title;
|
||||||
std::vector<char> placeholder;
|
std::vector<char> placeholder;
|
||||||
|
const OrbisImeParamExtended* extended_param_ = nullptr;
|
||||||
// A character can hold up to 4 bytes in UTF-8
|
// A character can hold up to 4 bytes in UTF-8
|
||||||
Common::CString<ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4> current_text;
|
Common::CString<ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4> current_text;
|
||||||
|
|
||||||
|
// Optional custom keyboard style (from extended params)
|
||||||
|
bool has_custom_style = false;
|
||||||
|
KeyboardStyle custom_kb_style{};
|
||||||
|
int caret_index = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/*──────────────── constructors / rule‑of‑five ────────────────*/
|
||||||
ImeDialogState(const OrbisImeDialogParam* param = nullptr,
|
ImeDialogState(const OrbisImeDialogParam* param = nullptr,
|
||||||
const OrbisImeParamExtended* extended = nullptr);
|
const OrbisImeParamExtended* extended = nullptr);
|
||||||
ImeDialogState(const ImeDialogState& other) = delete;
|
ImeDialogState(const ImeDialogState& other) = delete;
|
||||||
|
@ -44,6 +57,89 @@ public:
|
||||||
|
|
||||||
bool CopyTextToOrbisBuffer();
|
bool CopyTextToOrbisBuffer();
|
||||||
bool CallTextFilter();
|
bool CallTextFilter();
|
||||||
|
/*──────────────────── public read helpers ───────────────────*/
|
||||||
|
bool IsMultiLine() const {
|
||||||
|
return is_multi_line;
|
||||||
|
}
|
||||||
|
bool IsNumeric() const {
|
||||||
|
return is_numeric;
|
||||||
|
}
|
||||||
|
u32 MaxTextLength() const {
|
||||||
|
return max_text_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* TitleUtf8() const {
|
||||||
|
return title.empty() ? nullptr : title.data();
|
||||||
|
}
|
||||||
|
const char* PlaceholderUtf8() const {
|
||||||
|
return placeholder.empty() ? nullptr : placeholder.data();
|
||||||
|
}
|
||||||
|
const char* CurrentTextUtf8() const {
|
||||||
|
return current_text.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const OrbisImeParamExtended* GetExtendedParam() const {
|
||||||
|
return extended_param_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*─────────────────── public write helpers ───────────────────*/
|
||||||
|
// Replace the whole text buffer
|
||||||
|
void SetTextUtf8(const char* utf8) {
|
||||||
|
if (!utf8)
|
||||||
|
return;
|
||||||
|
std::strncpy(current_text.begin(), utf8, current_text.capacity() - 1);
|
||||||
|
current_text[current_text.capacity() - 1] = '\0';
|
||||||
|
input_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertUtf8AtCaret(const char* utf8, std::size_t len) {
|
||||||
|
if (!utf8 || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::size_t old_len = std::strlen(current_text.begin());
|
||||||
|
if (old_len + len >= current_text.capacity())
|
||||||
|
return; // full, silently ignore
|
||||||
|
|
||||||
|
// Move the text after caret forward
|
||||||
|
char* text_begin = current_text.begin();
|
||||||
|
std::memmove(text_begin + caret_index + len, text_begin + caret_index,
|
||||||
|
old_len - caret_index + 1); // +1 for null-terminator
|
||||||
|
|
||||||
|
// Copy the inserted text at caret position
|
||||||
|
std::memcpy(text_begin + caret_index, utf8, len);
|
||||||
|
|
||||||
|
caret_index += (int)len; // Move caret after inserted text
|
||||||
|
input_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove one UTF‑8 code‑point from the end (safe backspace)
|
||||||
|
void BackspaceUtf8() {
|
||||||
|
Utf8SafeBackspace(current_text.begin());
|
||||||
|
input_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackspaceUtf8AtCaret() {
|
||||||
|
char* buf = current_text.begin();
|
||||||
|
size_t len = std::strlen(buf);
|
||||||
|
|
||||||
|
if (caret_index == 0 || len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find byte index just before caret (start of previous codepoint)
|
||||||
|
int remove_start = caret_index - 1;
|
||||||
|
while (remove_start > 0 &&
|
||||||
|
(static_cast<unsigned char>(buf[remove_start]) & 0b11000000) == 0b10000000)
|
||||||
|
--remove_start;
|
||||||
|
|
||||||
|
int remove_len = caret_index - remove_start;
|
||||||
|
|
||||||
|
// Shift everything after caret to the left
|
||||||
|
std::memmove(buf + remove_start, buf + caret_index,
|
||||||
|
len - caret_index + 1); // +1 to move null terminator
|
||||||
|
caret_index = remove_start;
|
||||||
|
|
||||||
|
input_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status);
|
bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status);
|
||||||
|
@ -54,7 +150,11 @@ private:
|
||||||
char16_t* orbis_text, std::size_t orbis_text_len);
|
char16_t* orbis_text, std::size_t orbis_text_len);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// ImeDialogUi — draws the IME dialog & on‑screen keyboard
|
||||||
|
//---------------------------------------------------------------------
|
||||||
class ImeDialogUi final : public ImGui::Layer {
|
class ImeDialogUi final : public ImGui::Layer {
|
||||||
|
/*────────── private data ─────────*/
|
||||||
ImeDialogState* state{};
|
ImeDialogState* state{};
|
||||||
OrbisImeDialogStatus* status{};
|
OrbisImeDialogStatus* status{};
|
||||||
OrbisImeDialogResult* result{};
|
OrbisImeDialogResult* result{};
|
||||||
|
@ -62,7 +162,13 @@ class ImeDialogUi final : public ImGui::Layer {
|
||||||
bool first_render = true;
|
bool first_render = true;
|
||||||
std::mutex draw_mutex;
|
std::mutex draw_mutex;
|
||||||
|
|
||||||
|
OrbisImeParamExtended ext_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Global pointer to the active dialog‑UI (used by the callback bridge)
|
||||||
|
static ImeDialogUi* g_activeImeDialogUi;
|
||||||
|
|
||||||
|
/*───────── ctors / dtor ─────────*/
|
||||||
explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr,
|
explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr,
|
||||||
OrbisImeDialogResult* result = nullptr);
|
OrbisImeDialogResult* result = nullptr);
|
||||||
~ImeDialogUi() override;
|
~ImeDialogUi() override;
|
||||||
|
@ -70,15 +176,33 @@ public:
|
||||||
ImeDialogUi(ImeDialogUi&& other) noexcept;
|
ImeDialogUi(ImeDialogUi&& other) noexcept;
|
||||||
ImeDialogUi& operator=(ImeDialogUi&& other);
|
ImeDialogUi& operator=(ImeDialogUi&& other);
|
||||||
|
|
||||||
|
/*────────── main draw ───────────*/
|
||||||
void Draw() override;
|
void Draw() override;
|
||||||
|
|
||||||
|
/*────────── keyboard events ─────*/
|
||||||
|
void OnVirtualKeyEvent(const VirtualKeyEvent* evt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/*── helpers ─*/
|
||||||
void Free();
|
void Free();
|
||||||
|
|
||||||
void DrawInputText();
|
void DrawInputText();
|
||||||
void DrawMultiLineInputText();
|
void DrawMultiLineInputText();
|
||||||
|
|
||||||
static int InputTextCallback(ImGuiInputTextCallbackData* data);
|
static int InputTextCallback(ImGuiInputTextCallbackData* data);
|
||||||
|
|
||||||
|
void DrawTitle();
|
||||||
|
|
||||||
|
void DrawOkAndCancelButtons();
|
||||||
|
|
||||||
|
/*── keyboard section ─*/
|
||||||
|
KeyboardMode kb_mode = KeyboardMode::Letters;
|
||||||
|
ShiftState shift_state = ShiftState::None;
|
||||||
|
u64 kb_language = 0;
|
||||||
|
KeyboardStyle kb_style;
|
||||||
|
|
||||||
|
void DrawVirtualKeyboardSection();
|
||||||
|
void DrawPredictionBarAnCancelButton();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libraries::ImeDialog
|
} // namespace Libraries::ImeDialog
|
||||||
|
|
465
src/core/libraries/ime/ime_keyboard_layouts.cpp
Normal file
465
src/core/libraries/ime/ime_keyboard_layouts.cpp
Normal file
|
@ -0,0 +1,465 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/libraries/ime/ime_keyboard_layouts.h"
|
||||||
|
|
||||||
|
int c16rtomb(char* out, char16_t ch) {
|
||||||
|
if (ch <= 0x7F) {
|
||||||
|
out[0] = static_cast<char>(ch);
|
||||||
|
out[1] = '\0';
|
||||||
|
return 1;
|
||||||
|
} else if (ch <= 0x7FF) {
|
||||||
|
out[0] = 0xC0 | ((ch >> 6) & 0x1F);
|
||||||
|
out[1] = 0x80 | (ch & 0x3F);
|
||||||
|
out[2] = '\0';
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
out[0] = 0xE0 | ((ch >> 12) & 0x0F);
|
||||||
|
out[1] = 0x80 | ((ch >> 6) & 0x3F);
|
||||||
|
out[2] = 0x80 | (ch & 0x3F);
|
||||||
|
out[3] = '\0';
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<KeyEntry> kLayoutEnLettersUppercase = {
|
||||||
|
// Row 0
|
||||||
|
{0x31, u'1', KeyType::Character, 0, 0, 1, 1, "1", "", {None, None}},
|
||||||
|
{0x32, u'2', KeyType::Character, 0, 1, 1, 1, "2", "", {None, None}},
|
||||||
|
{0x33, u'3', KeyType::Character, 0, 2, 1, 1, "3", "", {None, None}},
|
||||||
|
{0x34, u'4', KeyType::Character, 0, 3, 1, 1, "4", "", {None, None}},
|
||||||
|
{0x35, u'5', KeyType::Character, 0, 4, 1, 1, "5", "", {None, None}},
|
||||||
|
{0x36, u'6', KeyType::Character, 0, 5, 1, 1, "6", "", {None, None}},
|
||||||
|
{0x37, u'7', KeyType::Character, 0, 6, 1, 1, "7", "", {None, None}},
|
||||||
|
{0x38, u'8', KeyType::Character, 0, 7, 1, 1, "8", "", {None, None}},
|
||||||
|
{0x39, u'9', KeyType::Character, 0, 8, 1, 1, "9", "", {None, None}},
|
||||||
|
{0x30, u'0', KeyType::Character, 0, 9, 1, 1, "0", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
{0x51, u'Q', KeyType::Character, 1, 0, 1, 1, "Q", "", {None, None}},
|
||||||
|
{0x57, u'W', KeyType::Character, 1, 1, 1, 1, "W", "", {None, None}},
|
||||||
|
{0x45, u'E', KeyType::Character, 1, 2, 1, 1, "E", "", {None, None}},
|
||||||
|
{0x52, u'R', KeyType::Character, 1, 3, 1, 1, "R", "", {None, None}},
|
||||||
|
{0x54, u'T', KeyType::Character, 1, 4, 1, 1, "T", "", {None, None}},
|
||||||
|
{0x59, u'Y', KeyType::Character, 1, 5, 1, 1, "Y", "", {None, None}},
|
||||||
|
{0x55, u'U', KeyType::Character, 1, 6, 1, 1, "U", "", {None, None}},
|
||||||
|
{0x49, u'I', KeyType::Character, 1, 7, 1, 1, "I", "", {None, None}},
|
||||||
|
{0x4F, u'O', KeyType::Character, 1, 8, 1, 1, "O", "", {None, None}},
|
||||||
|
{0x50, u'P', KeyType::Character, 1, 9, 1, 1, "P", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x41, u'A', KeyType::Character, 2, 0, 1, 1, "A", "", {None, None}},
|
||||||
|
{0x53, u'S', KeyType::Character, 2, 1, 1, 1, "S", "", {None, None}},
|
||||||
|
{0x44, u'D', KeyType::Character, 2, 2, 1, 1, "D", "", {None, None}},
|
||||||
|
{0x46, u'F', KeyType::Character, 2, 3, 1, 1, "F", "", {None, None}},
|
||||||
|
{0x47, u'G', KeyType::Character, 2, 4, 1, 1, "G", "", {None, None}},
|
||||||
|
{0x48, u'H', KeyType::Character, 2, 5, 1, 1, "H", "", {None, None}},
|
||||||
|
{0x4A, u'J', KeyType::Character, 2, 6, 1, 1, "J", "", {None, None}},
|
||||||
|
{0x4B, u'K', KeyType::Character, 2, 7, 1, 1, "K", "", {None, None}},
|
||||||
|
{0x4C, u'L', KeyType::Character, 2, 8, 1, 1, "L", "", {None, None}},
|
||||||
|
{0x22, u'"', KeyType::Character, 2, 9, 1, 1, "\"", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{0x5A, u'Z', KeyType::Character, 3, 0, 1, 1, "Z", "", {None, None}},
|
||||||
|
{0x58, u'X', KeyType::Character, 3, 1, 1, 1, "X", "", {None, None}},
|
||||||
|
{0x43, u'C', KeyType::Character, 3, 2, 1, 1, "C", "", {None, None}},
|
||||||
|
{0x56, u'V', KeyType::Character, 3, 3, 1, 1, "V", "", {None, None}},
|
||||||
|
{0x42, u'B', KeyType::Character, 3, 4, 1, 1, "B", "", {None, None}},
|
||||||
|
{0x4E, u'N', KeyType::Character, 3, 5, 1, 1, "N", "", {None, None}},
|
||||||
|
{0x4D, u'M', KeyType::Character, 3, 6, 1, 1, "M", "", {None, None}},
|
||||||
|
{0x2D, u'-', KeyType::Character, 3, 7, 1, 1, "-", "", {None, None}},
|
||||||
|
{0x5F, u'_', KeyType::Character, 3, 8, 1, 1, "_", "", {None, None}},
|
||||||
|
{0x2F, u'/', KeyType::Character, 3, 9, 1, 1, "/", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x10, u'\0', KeyType::Function, 4, 0, 1, 1, "⬆", "L2", {L2, None}},
|
||||||
|
{KC_SYM1, u'\0', KeyType::Function, 4, 1, 1, 1, "@#:", "△", {Triangle, None}}, // TODO:
|
||||||
|
{KC_ACCENTS, u'\0', KeyType::Function, 4, 2, 1, 1, "à", "", {None, None}}, // TODO:
|
||||||
|
{0x20, u' ', KeyType::Character, 4, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x08, u'\0', KeyType::Function, 4, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0xF020, u'\0', KeyType::Function, 5, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 5, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 5, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 5, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 5, 4, 1, 1, "KB", "", {None, None}}, // TODO:
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 5, 5, 1, 1, "...", "", {None, None}},
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 5, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO:
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0D, u'\r', KeyType::Function, 5, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<KeyEntry> kLayoutEnLettersLowercase = {
|
||||||
|
// Row 0
|
||||||
|
{0x31, u'1', KeyType::Character, 0, 0, 1, 1, "1", "", {None, None}},
|
||||||
|
{0x32, u'2', KeyType::Character, 0, 1, 1, 1, "2", "", {None, None}},
|
||||||
|
{0x33, u'3', KeyType::Character, 0, 2, 1, 1, "3", "", {None, None}},
|
||||||
|
{0x34, u'4', KeyType::Character, 0, 3, 1, 1, "4", "", {None, None}},
|
||||||
|
{0x35, u'5', KeyType::Character, 0, 4, 1, 1, "5", "", {None, None}},
|
||||||
|
{0x36, u'6', KeyType::Character, 0, 5, 1, 1, "6", "", {None, None}},
|
||||||
|
{0x37, u'7', KeyType::Character, 0, 6, 1, 1, "7", "", {None, None}},
|
||||||
|
{0x38, u'8', KeyType::Character, 0, 7, 1, 1, "8", "", {None, None}},
|
||||||
|
{0x39, u'9', KeyType::Character, 0, 8, 1, 1, "9", "", {None, None}},
|
||||||
|
{0x30, u'0', KeyType::Character, 0, 9, 1, 1, "0", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
{0x71, u'q', KeyType::Character, 1, 0, 1, 1, "q", "", {None, None}},
|
||||||
|
{0x77, u'w', KeyType::Character, 1, 1, 1, 1, "w", "", {None, None}},
|
||||||
|
{0x65, u'e', KeyType::Character, 1, 2, 1, 1, "e", "", {None, None}},
|
||||||
|
{0x72, u'r', KeyType::Character, 1, 3, 1, 1, "r", "", {None, None}},
|
||||||
|
{0x74, u't', KeyType::Character, 1, 4, 1, 1, "t", "", {None, None}},
|
||||||
|
{0x79, u'y', KeyType::Character, 1, 5, 1, 1, "y", "", {None, None}},
|
||||||
|
{0x75, u'u', KeyType::Character, 1, 6, 1, 1, "u", "", {None, None}},
|
||||||
|
{0x69, u'i', KeyType::Character, 1, 7, 1, 1, "i", "", {None, None}},
|
||||||
|
{0x6F, u'o', KeyType::Character, 1, 8, 1, 1, "o", "", {None, None}},
|
||||||
|
{0x70, u'p', KeyType::Character, 1, 9, 1, 1, "p", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x61, u'a', KeyType::Character, 2, 0, 1, 1, "a", "", {None, None}},
|
||||||
|
{0x73, u's', KeyType::Character, 2, 1, 1, 1, "s", "", {None, None}},
|
||||||
|
{0x64, u'd', KeyType::Character, 2, 2, 1, 1, "d", "", {None, None}},
|
||||||
|
{0x66, u'f', KeyType::Character, 2, 3, 1, 1, "f", "", {None, None}},
|
||||||
|
{0x67, u'g', KeyType::Character, 2, 4, 1, 1, "g", "", {None, None}},
|
||||||
|
{0x68, u'h', KeyType::Character, 2, 5, 1, 1, "h", "", {None, None}},
|
||||||
|
{0x6A, u'j', KeyType::Character, 2, 6, 1, 1, "j", "", {None, None}},
|
||||||
|
{0x6B, u'k', KeyType::Character, 2, 7, 1, 1, "k", "", {None, None}},
|
||||||
|
{0x6C, u'l', KeyType::Character, 2, 8, 1, 1, "l", "", {None, None}},
|
||||||
|
{0x22, u'"', KeyType::Character, 2, 9, 1, 1, "\"", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{0x7A, u'z', KeyType::Character, 3, 0, 1, 1, "z", "", {None, None}},
|
||||||
|
{0x78, u'x', KeyType::Character, 3, 1, 1, 1, "x", "", {None, None}},
|
||||||
|
{0x63, u'c', KeyType::Character, 3, 2, 1, 1, "c", "", {None, None}},
|
||||||
|
{0x76, u'v', KeyType::Character, 3, 3, 1, 1, "v", "", {None, None}},
|
||||||
|
{0x62, u'b', KeyType::Character, 3, 4, 1, 1, "b", "", {None, None}},
|
||||||
|
{0x6E, u'n', KeyType::Character, 3, 5, 1, 1, "n", "", {None, None}},
|
||||||
|
{0x6D, u'm', KeyType::Character, 3, 6, 1, 1, "m", "", {None, None}},
|
||||||
|
{0x2D, u'-', KeyType::Character, 3, 7, 1, 1, "-", "", {None, None}},
|
||||||
|
{0x5F, u'_', KeyType::Character, 3, 8, 1, 1, "_", "", {None, None}},
|
||||||
|
{0x2F, u'/', KeyType::Character, 3, 9, 1, 1, "/", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x105, u'\0', KeyType::Function, 4, 0, 1, 1, "⬆", "L2", {L2, None}},
|
||||||
|
{KC_SYM1, u'\0', KeyType::Function, 4, 1, 1, 1, "@#:", "△", {Triangle, None}}, // TODO
|
||||||
|
{KC_ACCENTS, u'\0', KeyType::Function, 4, 2, 1, 1, "à", "", {None, None}}, // TODO
|
||||||
|
{0x20, u' ', KeyType::Character, 4, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x00, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x08, u'\0', KeyType::Function, 4, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0xF020, u'\0', KeyType::Function, 5, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 5, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 5, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 5, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 5, 4, 1, 1, "KB", "", {None, None}}, // TODO
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 5, 5, 1, 1, "...", "", {None, None}}, // TODO
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 5, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0D, u'\r', KeyType::Function, 5, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// From PS5
|
||||||
|
const std::vector<KeyEntry> kLayoutEnSymbols1 = {
|
||||||
|
// Row 1
|
||||||
|
{0x21, u'!', KeyType::Character, 0, 0, 1, 1, "!", "", {None, None}},
|
||||||
|
{0x3F, u'?', KeyType::Character, 0, 1, 1, 1, "?", "", {None, None}},
|
||||||
|
{0x22, u'"', KeyType::Character, 0, 2, 1, 1, "\"", "", {None, None}},
|
||||||
|
{0x27, u'\'', KeyType::Character, 0, 3, 1, 1, "'", "", {None, None}},
|
||||||
|
{0x23, u'#', KeyType::Character, 0, 4, 1, 1, "#", "", {None, None}},
|
||||||
|
{0x25, u'%', KeyType::Character, 0, 5, 1, 1, "%", "", {None, None}},
|
||||||
|
{0x28, u'(', KeyType::Character, 0, 6, 1, 1, "(", "", {None, None}},
|
||||||
|
{0x29, u')', KeyType::Character, 0, 7, 1, 1, ")", "", {None, None}},
|
||||||
|
{0xF001, u'\0', KeyType::Function, 0, 8, 1, 1, "()", "", {None, None}},
|
||||||
|
{0x2F, u'/', KeyType::Character, 0, 9, 1, 1, "/", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x2D, u'-', KeyType::Character, 1, 0, 1, 1, "-", "", {None, None}},
|
||||||
|
{0x5F, u'_', KeyType::Character, 1, 1, 1, 1, "_", "", {None, None}},
|
||||||
|
{0x2C, u',', KeyType::Character, 1, 2, 1, 1, ",", "", {None, None}},
|
||||||
|
{0x2E, u'.', KeyType::Character, 1, 3, 1, 1, ".", "", {None, None}},
|
||||||
|
{0x3A, u':', KeyType::Character, 1, 4, 1, 1, ":", "", {None, None}},
|
||||||
|
{0x3B, u';', KeyType::Character, 1, 5, 1, 1, ";", "", {None, None}},
|
||||||
|
{0x2A, u'*', KeyType::Character, 1, 6, 1, 1, "*", "", {None, None}},
|
||||||
|
{0x26, u'&', KeyType::Character, 1, 7, 1, 1, "&", "", {None, None}},
|
||||||
|
{0x2B, u'+', KeyType::Character, 1, 8, 1, 1, "+", "", {None, None}},
|
||||||
|
{0x3D, u'=', KeyType::Character, 1, 9, 1, 1, "=", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{0x3C, u'<', KeyType::Character, 2, 0, 1, 1, "<", "", {None, None}},
|
||||||
|
{0x3E, u'>', KeyType::Character, 2, 1, 1, 1, ">", "", {None, None}},
|
||||||
|
{0x40, u'@', KeyType::Character, 2, 2, 1, 1, "@", "", {None, None}},
|
||||||
|
{0x5B, u'[', KeyType::Character, 2, 3, 1, 1, "[", "", {None, None}},
|
||||||
|
{0x5D, u']', KeyType::Character, 2, 4, 1, 1, "]", "", {None, None}},
|
||||||
|
{0xF002, u'\0', KeyType::Function, 2, 5, 1, 1, "[]", "", {None, None}},
|
||||||
|
{0x7B, u'{', KeyType::Character, 2, 6, 1, 1, "{", "", {None, None}},
|
||||||
|
{0x7D, u'}', KeyType::Character, 2, 7, 1, 1, "}", "", {None, None}},
|
||||||
|
{0xF004, u'\0', KeyType::Function, 2, 8, 1, 1, "{}", "", {None, None}},
|
||||||
|
{KC_SYM2, u'\0', KeyType::Function, 2, 9, 1, 2, "→", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x5C, u'\\', KeyType::Character, 3, 0, 1, 1, "\\", "", {None, None}},
|
||||||
|
{0x7C, u'|', KeyType::Character, 3, 1, 1, 1, "|", "", {None, None}},
|
||||||
|
{0x5E, u'^', KeyType::Character, 3, 2, 1, 1, "^", "", {None, None}},
|
||||||
|
{0x60, u'`', KeyType::Character, 3, 3, 1, 1, "`", "", {None, None}},
|
||||||
|
{0x24, u'$', KeyType::Character, 3, 4, 1, 1, "$", "", {None, None}},
|
||||||
|
{0x20AC, u'\u20AC', KeyType::Character, 3, 5, 1, 1, "€", "", {None, None}},
|
||||||
|
{0x00A3, u'\u00A3', KeyType::Character, 3, 6, 1, 1, "£", "", {None, None}},
|
||||||
|
{0x00A5, u'\u00A5', KeyType::Character, 3, 7, 1, 1, "¥", "", {None, None}},
|
||||||
|
{0x20A9, u'\u20A9', KeyType::Character, 3, 8, 1, 1, "₩", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 0, 1, 1, "", "", {None, None}},
|
||||||
|
{KC_LETTERS, u'\0', KeyType::Function, 4, 1, 1, 1, "ABC", "L2+△", {L2, Triangle}}, // TODO:
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 2, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0020, u' ', KeyType::Character, 4, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0008, u'\0', KeyType::Function, 4, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
{0xF020, u'\0', KeyType::Function, 5, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 5, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 5, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 5, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 5, 4, 1, 1, "KB", "", {None, None}}, // TODO:
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 5, 5, 1, 1, "...", "", {None, None}}, // TODO:
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 5, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO:
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x000D, u'\r', KeyType::Function, 5, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// From PS5
|
||||||
|
const std::vector<KeyEntry> kLayoutEnSymbols2 = {
|
||||||
|
// Row 1
|
||||||
|
{0x201C, u'“', KeyType::Character, 0, 0, 1, 1, "“", "", {None, None}},
|
||||||
|
{0x201D, u'”', KeyType::Character, 0, 1, 1, 1, "”", "", {None, None}},
|
||||||
|
{0x201E, u'„', KeyType::Character, 0, 2, 1, 1, "„", "", {None, None}},
|
||||||
|
{0x00A1, u'¡', KeyType::Character, 0, 3, 1, 1, "¡", "", {None, None}},
|
||||||
|
{0xF013, u'\0', KeyType::Function, 0, 4, 1, 1, "¡!", "", {None, None}},
|
||||||
|
{0x00BF, u'¿', KeyType::Character, 0, 5, 1, 1, "¿", "", {None, None}},
|
||||||
|
{0xF014, u'\0', KeyType::Function, 0, 6, 1, 1, "¿?", "", {None, None}},
|
||||||
|
{0x007E, u'~', KeyType::Character, 0, 7, 1, 1, "~", "", {None, None}},
|
||||||
|
{0x00B7, u'·', KeyType::Character, 0, 8, 1, 1, "·", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 0, 9, 1, 1, "", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x00D7, u'×', KeyType::Character, 1, 0, 1, 1, "×", "", {None, None}},
|
||||||
|
{0x00F7, u'÷', KeyType::Character, 1, 1, 1, 1, "÷", "", {None, None}},
|
||||||
|
{0x2039, u'‹', KeyType::Character, 1, 2, 1, 1, "‹", "", {None, None}},
|
||||||
|
{0x203A, u'›', KeyType::Character, 1, 3, 1, 1, "›", "", {None, None}},
|
||||||
|
{0x00AB, u'«', KeyType::Character, 1, 4, 1, 1, "«", "", {None, None}},
|
||||||
|
{0x00BB, u'»', KeyType::Character, 1, 5, 1, 1, "»", "", {None, None}},
|
||||||
|
{0x00BA, u'º', KeyType::Character, 1, 6, 1, 1, "º", "", {None, None}},
|
||||||
|
{0x00AA, u'ª', KeyType::Character, 1, 7, 1, 1, "ª", "", {None, None}},
|
||||||
|
{0x00B0, u'°', KeyType::Character, 1, 8, 1, 1, "°", "", {None, None}},
|
||||||
|
{0x00A7, u'§', KeyType::Character, 1, 9, 1, 1, "§", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{KC_SYM1, u'\0', KeyType::Function, 2, 0, 1, 2, "←", "", {None, None}},
|
||||||
|
{0x00A6, u'¦', KeyType::Character, 2, 1, 1, 1, "¦", "", {None, None}},
|
||||||
|
{0x00B5, u'µ', KeyType::Character, 2, 2, 1, 1, "µ", "", {None, None}},
|
||||||
|
{0x00AC, u'¬', KeyType::Character, 2, 3, 1, 1, "¬", "", {None, None}},
|
||||||
|
{0x00B9, u'¹', KeyType::Character, 2, 4, 1, 1, "¹", "", {None, None}},
|
||||||
|
{0x00B2, u'²', KeyType::Character, 2, 5, 1, 1, "²", "", {None, None}},
|
||||||
|
{0x00B3, u'³', KeyType::Character, 2, 6, 1, 1, "³", "", {None, None}},
|
||||||
|
{0x00BC, u'¼', KeyType::Character, 2, 7, 1, 1, "¼", "", {None, None}},
|
||||||
|
{0x00BD, u'½', KeyType::Character, 2, 8, 1, 1, "½", "", {None, None}},
|
||||||
|
{0x00BE, u'¾', KeyType::Character, 2, 9, 1, 1, "¾", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x00A2, u'¢', KeyType::Character, 3, 1, 1, 1, "¢", "", {None, None}},
|
||||||
|
{0x00A4, u'¤', KeyType::Character, 3, 2, 1, 1, "¤", "", {None, None}},
|
||||||
|
{0x2019, u'’', KeyType::Character, 3, 3, 1, 1, "’", "", {None, None}},
|
||||||
|
{0x2018, u'‘', KeyType::Character, 3, 4, 1, 1, "‘", "", {None, None}},
|
||||||
|
{0x201B, u'‛', KeyType::Character, 3, 5, 1, 1, "‛", "", {None, None}},
|
||||||
|
{0x201A, u'‚', KeyType::Character, 3, 6, 1, 1, "‚", "", {None, None}},
|
||||||
|
{0x2116, u'№', KeyType::Character, 3, 7, 1, 1, "№", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 3, 8, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 3, 9, 1, 1, "", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 0, 1, 1, "", "", {None, None}},
|
||||||
|
{KC_LETTERS, u'\0', KeyType::Function, 4, 1, 1, 1, "ABC", "L2+△", {L2, Triangle}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 2, 1, 1, "", "", {None, None}},
|
||||||
|
{0x20, u' ', KeyType::Character, 4, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x08, u'\0', KeyType::Function, 4, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
{0xF020, u'\0', KeyType::Function, 5, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 5, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 5, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 5, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 5, 4, 1, 1, "KB", "", {None, None}}, // TODO
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 5, 5, 1, 1, "...", "", {None, None}}, // TODO
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 5, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0D, u'\r', KeyType::Function, 5, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<KeyEntry> kLayoutEnAccentLettersUppercase = {
|
||||||
|
// Row 0
|
||||||
|
{0x00C0, u'À', KeyType::Character, 0, 0, 1, 1, "À", "", {None, None}},
|
||||||
|
{0x00C1, u'Á', KeyType::Character, 0, 1, 1, 1, "Á", "", {None, None}},
|
||||||
|
{0x00C2, u'Â', KeyType::Character, 0, 2, 1, 1, "Â", "", {None, None}},
|
||||||
|
{0x00C3, u'Ã', KeyType::Character, 0, 3, 1, 1, "Ã", "", {None, None}},
|
||||||
|
{0x00C4, u'Ä', KeyType::Character, 0, 4, 1, 1, "Ä", "", {None, None}},
|
||||||
|
{0x00C5, u'Å', KeyType::Character, 0, 5, 1, 1, "Å", "", {None, None}},
|
||||||
|
{0x0104, u'Ą', KeyType::Character, 0, 6, 1, 1, "Ą", "", {None, None}},
|
||||||
|
{0x00C6, u'Æ', KeyType::Character, 0, 7, 1, 1, "Æ", "", {None, None}},
|
||||||
|
{0x00C7, u'Ç', KeyType::Character, 0, 8, 1, 1, "Ç", "", {None, None}},
|
||||||
|
{0x0106, u'Ć', KeyType::Character, 0, 9, 1, 1, "Ć", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
{0x00C8, u'È', KeyType::Character, 1, 0, 1, 1, "È", "", {None, None}},
|
||||||
|
{0x00C9, u'É', KeyType::Character, 1, 1, 1, 1, "É", "", {None, None}},
|
||||||
|
{0x00CA, u'Ê', KeyType::Character, 1, 2, 1, 1, "Ê", "", {None, None}},
|
||||||
|
{0x00CB, u'Ë', KeyType::Character, 1, 3, 1, 1, "Ë", "", {None, None}},
|
||||||
|
{0x0118, u'Ę', KeyType::Character, 1, 4, 1, 1, "Ę", "", {None, None}},
|
||||||
|
{0x011E, u'Ğ', KeyType::Character, 1, 5, 1, 1, "Ğ", "", {None, None}},
|
||||||
|
{0x00CC, u'Ì', KeyType::Character, 1, 6, 1, 1, "Ì", "", {None, None}},
|
||||||
|
{0x00CD, u'Í', KeyType::Character, 1, 7, 1, 1, "Í", "", {None, None}},
|
||||||
|
{0x00CE, u'Î', KeyType::Character, 1, 8, 1, 1, "Î", "", {None, None}},
|
||||||
|
{0x00CF, u'Ï', KeyType::Character, 1, 9, 1, 1, "Ï", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x0130, u'İ', KeyType::Character, 2, 0, 1, 1, "İ", "", {None, None}},
|
||||||
|
{0x0141, u'Ł', KeyType::Character, 2, 1, 1, 1, "Ł", "", {None, None}},
|
||||||
|
{0x00D1, u'Ñ', KeyType::Character, 2, 2, 1, 1, "Ñ", "", {None, None}},
|
||||||
|
{0x0143, u'Ń', KeyType::Character, 2, 3, 1, 1, "Ń", "", {None, None}},
|
||||||
|
{0x00D2, u'Ò', KeyType::Character, 2, 4, 1, 1, "Ò", "", {None, None}},
|
||||||
|
{0x00D3, u'Ó', KeyType::Character, 2, 5, 1, 1, "Ó", "", {None, None}},
|
||||||
|
{0x00D4, u'Ô', KeyType::Character, 2, 6, 1, 1, "Ô", "", {None, None}},
|
||||||
|
{0x00D5, u'Õ', KeyType::Character, 2, 7, 1, 1, "Õ", "", {None, None}},
|
||||||
|
{0x00D6, u'Ö', KeyType::Character, 2, 8, 1, 1, "Ö", "", {None, None}},
|
||||||
|
{0x00D8, u'Ø', KeyType::Character, 2, 9, 1, 1, "Ø", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{0x0152, u'Œ', KeyType::Character, 3, 0, 1, 1, "Œ", "", {None, None}},
|
||||||
|
{0x015A, u'Ś', KeyType::Character, 3, 1, 1, 1, "Ś", "", {None, None}},
|
||||||
|
{0x015E, u'Ş', KeyType::Character, 3, 2, 1, 1, "Ş", "", {None, None}},
|
||||||
|
{0x0160, u'Š', KeyType::Character, 3, 3, 1, 1, "Š", "", {None, None}},
|
||||||
|
{0x00DF, u'ß', KeyType::Character, 3, 4, 1, 1, "ß", "", {None, None}},
|
||||||
|
{0x00D9, u'Ù', KeyType::Character, 3, 5, 1, 1, "Ù", "", {None, None}},
|
||||||
|
{0x00DA, u'Ú', KeyType::Character, 3, 6, 1, 1, "Ú", "", {None, None}},
|
||||||
|
{0x00DB, u'Û', KeyType::Character, 3, 7, 1, 1, "Û", "", {None, None}},
|
||||||
|
{0x00DC, u'Ü', KeyType::Character, 3, 8, 1, 1, "Ü", "", {None, None}},
|
||||||
|
{0x00DD, u'Ý', KeyType::Character, 3, 9, 1, 1, "Ý", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x0178, u'Ÿ', KeyType::Character, 4, 0, 1, 1, "Ÿ", "", {None, None}},
|
||||||
|
{0x0179, u'Ź', KeyType::Character, 4, 1, 1, 1, "Ź", "", {None, None}},
|
||||||
|
{0x017B, u'Ż', KeyType::Character, 4, 2, 1, 1, "Ż", "", {None, None}},
|
||||||
|
{0x017D, u'Ž', KeyType::Character, 4, 3, 1, 1, "Ž", "", {None, None}},
|
||||||
|
{0x00D0, u'Ð', KeyType::Character, 4, 4, 1, 1, "Ð", "", {None, None}},
|
||||||
|
{0x00DE, u'Þ', KeyType::Character, 4, 5, 1, 1, "Þ", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 6, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 8, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 9, 1, 1, "", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0x0010, u'\0', KeyType::Function, 5, 0, 1, 1, "⬆", "L2", {L2, None}},
|
||||||
|
{KC_LETTERS, u'\0', KeyType::Function, 5, 1, 1, 1, "ABC", "L2+△", {L3, Triangle}}, // TODO:
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 2, 1, 1, "", "", {None, None}}, // TODO:
|
||||||
|
{0x0020, u' ', KeyType::Character, 5, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0008, u'\0', KeyType::Function, 5, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
{0xF020, u'\0', KeyType::Function, 6, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 6, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 6, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 6, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 6, 4, 1, 1, "KB", "", {None, None}}, // TODO
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 6, 5, 1, 1, "...", "", {None, None}}, // TODO
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 6, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 6, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x000D, u'\r', KeyType::Function, 6, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<KeyEntry> kLayoutEnAccentLettersLowercase = {
|
||||||
|
// Row 0
|
||||||
|
{0x00E0, u'à', KeyType::Character, 0, 0, 1, 1, "à", "", {None, None}},
|
||||||
|
{0x00E1, u'á', KeyType::Character, 0, 1, 1, 1, "á", "", {None, None}},
|
||||||
|
{0x00E2, u'â', KeyType::Character, 0, 2, 1, 1, "â", "", {None, None}},
|
||||||
|
{0x00E3, u'ã', KeyType::Character, 0, 3, 1, 1, "ã", "", {None, None}},
|
||||||
|
{0x00E4, u'ä', KeyType::Character, 0, 4, 1, 1, "ä", "", {None, None}},
|
||||||
|
{0x00E5, u'å', KeyType::Character, 0, 5, 1, 1, "å", "", {None, None}},
|
||||||
|
{0x0105, u'ą', KeyType::Character, 0, 6, 1, 1, "ą", "", {None, None}},
|
||||||
|
{0x00E6, u'æ', KeyType::Character, 0, 7, 1, 1, "æ", "", {None, None}},
|
||||||
|
{0x00E7, u'ç', KeyType::Character, 0, 8, 1, 1, "ç", "", {None, None}},
|
||||||
|
{0x0107, u'ć', KeyType::Character, 0, 9, 1, 1, "ć", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
{0x00E8, u'è', KeyType::Character, 1, 0, 1, 1, "è", "", {None, None}},
|
||||||
|
{0x00E9, u'é', KeyType::Character, 1, 1, 1, 1, "é", "", {None, None}},
|
||||||
|
{0x00EA, u'ê', KeyType::Character, 1, 2, 1, 1, "ê", "", {None, None}},
|
||||||
|
{0x00EB, u'ë', KeyType::Character, 1, 3, 1, 1, "ë", "", {None, None}},
|
||||||
|
{0x0119, u'ę', KeyType::Character, 1, 4, 1, 1, "ę", "", {None, None}},
|
||||||
|
{0x011F, u'ğ', KeyType::Character, 1, 5, 1, 1, "ğ", "", {None, None}},
|
||||||
|
{0x00EC, u'ì', KeyType::Character, 1, 6, 1, 1, "ì", "", {None, None}},
|
||||||
|
{0x00ED, u'í', KeyType::Character, 1, 7, 1, 1, "í", "", {None, None}},
|
||||||
|
{0x00EE, u'î', KeyType::Character, 1, 8, 1, 1, "î", "", {None, None}},
|
||||||
|
{0x00EF, u'ï', KeyType::Character, 1, 9, 1, 1, "ï", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
{0x0131, u'ı', KeyType::Character, 2, 0, 1, 1, "ı", "", {None, None}},
|
||||||
|
{0x0142, u'ł', KeyType::Character, 2, 1, 1, 1, "ł", "", {None, None}},
|
||||||
|
{0x00F1, u'ñ', KeyType::Character, 2, 2, 1, 1, "ñ", "", {None, None}},
|
||||||
|
{0x0144, u'ń', KeyType::Character, 2, 3, 1, 1, "ń", "", {None, None}},
|
||||||
|
{0x00F2, u'ò', KeyType::Character, 2, 4, 1, 1, "ò", "", {None, None}},
|
||||||
|
{0x00F3, u'ó', KeyType::Character, 2, 5, 1, 1, "ó", "", {None, None}},
|
||||||
|
{0x00F4, u'ô', KeyType::Character, 2, 6, 1, 1, "ô", "", {None, None}},
|
||||||
|
{0x00F5, u'õ', KeyType::Character, 2, 7, 1, 1, "õ", "", {None, None}},
|
||||||
|
{0x00F6, u'ö', KeyType::Character, 2, 8, 1, 1, "ö", "", {None, None}},
|
||||||
|
{0x00F8, u'ø', KeyType::Character, 2, 9, 1, 1, "ø", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
{0x0153, u'œ', KeyType::Character, 3, 0, 1, 1, "œ", "", {None, None}},
|
||||||
|
{0x015B, u'ś', KeyType::Character, 3, 1, 1, 1, "ś", "", {None, None}},
|
||||||
|
{0x015F, u'ş', KeyType::Character, 3, 2, 1, 1, "ş", "", {None, None}},
|
||||||
|
{0x0161, u'š', KeyType::Character, 3, 3, 1, 1, "š", "", {None, None}},
|
||||||
|
{0x00DF, u'ß', KeyType::Character, 3, 4, 1, 1, "ß", "", {None, None}},
|
||||||
|
{0x00F9, u'ù', KeyType::Character, 3, 5, 1, 1, "ù", "", {None, None}},
|
||||||
|
{0x00FA, u'ú', KeyType::Character, 3, 6, 1, 1, "ú", "", {None, None}},
|
||||||
|
{0x00FB, u'û', KeyType::Character, 3, 7, 1, 1, "û", "", {None, None}},
|
||||||
|
{0x00FC, u'ü', KeyType::Character, 3, 8, 1, 1, "ü", "", {None, None}},
|
||||||
|
{0x00FD, u'ý', KeyType::Character, 3, 9, 1, 1, "ý", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 4
|
||||||
|
{0x00FF, u'ÿ', KeyType::Character, 4, 0, 1, 1, "ÿ", "", {None, None}},
|
||||||
|
{0x017A, u'ź', KeyType::Character, 4, 1, 1, 1, "ź", "", {None, None}},
|
||||||
|
{0x017C, u'ż', KeyType::Character, 4, 2, 1, 1, "ż", "", {None, None}},
|
||||||
|
{0x017E, u'ž', KeyType::Character, 4, 3, 1, 1, "ž", "", {None, None}},
|
||||||
|
{0x00F0, u'ð', KeyType::Character, 4, 4, 1, 1, "ð", "", {None, None}},
|
||||||
|
{0x00FE, u'þ', KeyType::Character, 4, 5, 1, 1, "þ", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 6, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 8, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 4, 9, 1, 1, "", "", {None, None}},
|
||||||
|
|
||||||
|
// Row 5
|
||||||
|
{0x0010, u'\0', KeyType::Function, 5, 0, 1, 1, "⬆", "L2", {L2, None}},
|
||||||
|
{KC_LETTERS, u'\0', KeyType::Function, 5, 1, 1, 1, "ABC", "L2+△", {L3, Triangle}}, // TODO
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 2, 1, 1, "", "", {None, None}}, // TODO
|
||||||
|
{0x0020, u' ', KeyType::Character, 5, 3, 4, 1, "Space", "△", {Triangle, None}},
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 5, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x0008, u'\0', KeyType::Function, 5, 8, 2, 1, "⇦", "□", {Square, None}, true},
|
||||||
|
|
||||||
|
// Row 6
|
||||||
|
{0xF020, u'\0', KeyType::Function, 6, 0, 1, 1, "▲", "", {Up, None}},
|
||||||
|
{0xF021, u'\0', KeyType::Function, 6, 1, 1, 1, "▼", "", {Down, None}},
|
||||||
|
{0xF022, u'\0', KeyType::Function, 6, 2, 1, 1, "◀", "L1", {L1, None}},
|
||||||
|
{0xF023, u'\0', KeyType::Function, 6, 3, 1, 1, "▶", "R1", {R1, None}},
|
||||||
|
{KC_KB, u'\0', KeyType::Function, 6, 4, 1, 1, "KB", "", {None, None}}, // TODO
|
||||||
|
{KC_OPT, u'\0', KeyType::Function, 6, 5, 1, 1, "...", "", {None, None}}, // TODO
|
||||||
|
{KC_GYRO, u'\0', KeyType::Function, 6, 6, 1, 1, "+/⊗", "R3", {R3, None}}, // TODO
|
||||||
|
{0x0000, u'\0', KeyType::Disabled, 6, 7, 1, 1, "", "", {None, None}},
|
||||||
|
{0x000D, u'\r', KeyType::Function, 6, 8, 2, 1, "Done", "R2", {R2, None}},
|
||||||
|
};
|
63
src/core/libraries/ime/ime_keyboard_layouts.h
Normal file
63
src/core/libraries/ime/ime_keyboard_layouts.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
enum class KeyType : u8 { Character = 0, Function = 1, Disabled = 2 };
|
||||||
|
|
||||||
|
struct KeyEntry {
|
||||||
|
u16 keycode; // 0xF100+ unused, so can be used as temporary defined keys for unknown
|
||||||
|
char16_t character;
|
||||||
|
KeyType type;
|
||||||
|
u8 row;
|
||||||
|
u8 col;
|
||||||
|
u8 colspan;
|
||||||
|
u8 rowspan;
|
||||||
|
const char* label;
|
||||||
|
const char* controller_hint;
|
||||||
|
ImGuiKey bound_buttons[2];
|
||||||
|
bool allow_repeat{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
int c16rtomb(char* out, char16_t ch);
|
||||||
|
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnLettersUppercase;
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnLettersLowercase;
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnAccentLettersUppercase;
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnAccentLettersLowercase;
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnSymbols1;
|
||||||
|
extern const std::vector<KeyEntry> kLayoutEnSymbols2;
|
||||||
|
|
||||||
|
constexpr ImGuiKey None = ImGuiKey::ImGuiKey_None;
|
||||||
|
constexpr ImGuiKey L1 = ImGuiKey::ImGuiKey_GamepadL1;
|
||||||
|
constexpr ImGuiKey R1 = ImGuiKey::ImGuiKey_GamepadR1;
|
||||||
|
constexpr ImGuiKey L2 = ImGuiKey::ImGuiKey_GamepadL2;
|
||||||
|
constexpr ImGuiKey R2 = ImGuiKey::ImGuiKey_GamepadR2;
|
||||||
|
constexpr ImGuiKey L3 = ImGuiKey::ImGuiKey_GamepadL3;
|
||||||
|
constexpr ImGuiKey R3 = ImGuiKey::ImGuiKey_GamepadR3;
|
||||||
|
constexpr ImGuiKey Up = ImGuiKey::ImGuiKey_GamepadDpadUp;
|
||||||
|
constexpr ImGuiKey Down = ImGuiKey::ImGuiKey_GamepadDpadDown;
|
||||||
|
constexpr ImGuiKey Left = ImGuiKey::ImGuiKey_GamepadDpadLeft;
|
||||||
|
constexpr ImGuiKey Right = ImGuiKey::ImGuiKey_GamepadDpadRight;
|
||||||
|
constexpr ImGuiKey Cross = ImGuiKey::ImGuiKey_GamepadFaceDown; // X button
|
||||||
|
constexpr ImGuiKey Circle = ImGuiKey::ImGuiKey_GamepadFaceRight; // O button
|
||||||
|
constexpr ImGuiKey Square = ImGuiKey::ImGuiKey_GamepadFaceLeft; // [] button
|
||||||
|
constexpr ImGuiKey Triangle = ImGuiKey::ImGuiKey_GamepadFaceUp; // /\ button
|
||||||
|
constexpr ImGuiKey Options = ImGuiKey::ImGuiKey_GraveAccent; // Options button
|
||||||
|
|
||||||
|
// Fake function keycodes
|
||||||
|
constexpr u16 KC_SYM1 = 0xF100;
|
||||||
|
constexpr u16 KC_SYM2 = 0xF101;
|
||||||
|
constexpr u16 KC_ACCENTS = 0xF102;
|
||||||
|
constexpr u16 KC_LETTERS = 0xF103;
|
||||||
|
constexpr u16 KC_KB = 0xF104;
|
||||||
|
constexpr u16 KC_GYRO = 0xF105;
|
||||||
|
constexpr u16 KC_OPT = 0xF106;
|
||||||
|
constexpr u16 KC_UP = 0xF020;
|
||||||
|
constexpr u16 KC_DOWN = 0xF021;
|
||||||
|
constexpr u16 KC_LEFT = 0xF022;
|
||||||
|
constexpr u16 KC_RIGHT = 0xF023;
|
222
src/core/libraries/ime/ime_keyboard_ui.cpp
Normal file
222
src/core/libraries/ime/ime_keyboard_ui.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
// #include <cstring>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include "common/cstring.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "core/libraries/ime/ime_common.h"
|
||||||
|
#include "core/libraries/ime/ime_keyboard_layouts.h"
|
||||||
|
#include "core/libraries/ime/ime_keyboard_ui.h"
|
||||||
|
#include "imgui/imgui_std.h"
|
||||||
|
|
||||||
|
using namespace ImGui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes one UTF-8 codepoint from the end of 'buffer', if present.
|
||||||
|
*/
|
||||||
|
void Utf8SafeBackspace(char* buffer) {
|
||||||
|
size_t len = std::strlen(buffer);
|
||||||
|
if (len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Move backward over any continuation bytes.
|
||||||
|
while (len > 0 && (static_cast<unsigned char>(buffer[len]) & 0b11000000) == 0b10000000) {
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
// Remove one codepoint.
|
||||||
|
buffer[len - 1] = '\0';
|
||||||
|
buffer[len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks which layout vector we want for OrbisImeType, kb_mode, shift_state, etc.
|
||||||
|
*/
|
||||||
|
const std::vector<KeyEntry>* GetKeyboardLayout(OrbisImeType type, KeyboardMode mode,
|
||||||
|
ShiftState shift, u64 language) {
|
||||||
|
switch (type) {
|
||||||
|
case OrbisImeType::Number:
|
||||||
|
// For numeric input, you might have a dedicated numeric layout,
|
||||||
|
// but here we reuse kLayoutEnSymbols1.
|
||||||
|
return &kLayoutEnSymbols1;
|
||||||
|
|
||||||
|
case OrbisImeType::Url:
|
||||||
|
case OrbisImeType::Mail:
|
||||||
|
// Use letters; uppercase if SHIFT is on.
|
||||||
|
if (shift == ShiftState::CapsLock || shift == ShiftState::Shift) {
|
||||||
|
return &kLayoutEnLettersUppercase;
|
||||||
|
} else {
|
||||||
|
return &kLayoutEnLettersLowercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OrbisImeType::BasicLatin:
|
||||||
|
case OrbisImeType::Default:
|
||||||
|
default:
|
||||||
|
switch (mode) {
|
||||||
|
case KeyboardMode::Symbols1:
|
||||||
|
return &kLayoutEnSymbols1;
|
||||||
|
case KeyboardMode::Symbols2:
|
||||||
|
return &kLayoutEnSymbols2;
|
||||||
|
case KeyboardMode::AccentLetters:
|
||||||
|
if (shift == ShiftState::CapsLock || shift == ShiftState::Shift) {
|
||||||
|
return &kLayoutEnAccentLettersUppercase;
|
||||||
|
} else {
|
||||||
|
return &kLayoutEnAccentLettersLowercase;
|
||||||
|
}
|
||||||
|
case KeyboardMode::Letters:
|
||||||
|
default:
|
||||||
|
if (shift == ShiftState::CapsLock || shift == ShiftState::Shift) {
|
||||||
|
return &kLayoutEnLettersUppercase;
|
||||||
|
} else {
|
||||||
|
return &kLayoutEnLettersLowercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the given layout using the style logic:
|
||||||
|
* - For symbols layout and if style.use_button_symbol_color is true,
|
||||||
|
* character keys get style.color_button_symbol.
|
||||||
|
* - Function keys get style.color_button_function.
|
||||||
|
* - The "Done"/"Enter" key (keycode 0x0D) gets style.color_special.
|
||||||
|
* - Otherwise, keys use style.color_button_default.
|
||||||
|
*
|
||||||
|
* This version retains all GUI layout details (positions, colors, sizes, etc.) exactly as in your
|
||||||
|
* base files. The only change is in key event detection: after drawing each key with Button(), we
|
||||||
|
* use IsItemActive() to determine the pressed state so that the backend key processing works
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
void RenderKeyboardLayout(const std::vector<KeyEntry>& layout, KeyboardMode mode,
|
||||||
|
void (*on_key_event)(const VirtualKeyEvent*),
|
||||||
|
const KeyboardStyle& style) {
|
||||||
|
ImGui::BeginGroup();
|
||||||
|
|
||||||
|
/* ─────────────── 1. grid size & cell metrics ─────────────── */
|
||||||
|
int max_col = 0, max_row = 0;
|
||||||
|
for (const KeyEntry& k : layout) {
|
||||||
|
max_col = std::max(max_col, k.col + (int)k.colspan);
|
||||||
|
max_row = std::max(max_row, k.row + (int)k.rowspan);
|
||||||
|
}
|
||||||
|
if (max_col == 0 || max_row == 0) {
|
||||||
|
ImGui::EndGroup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float pad = 20.0f;
|
||||||
|
const float spacing_w = (max_col - 1) * style.key_spacing;
|
||||||
|
const float spacing_h = (max_row - 1) * style.key_spacing;
|
||||||
|
const float cell_w = std::floor((style.layout_width - spacing_w - 2 * pad) / max_col + 0.5f);
|
||||||
|
const float cell_h = std::floor((style.layout_height - spacing_h - 85.0f) / max_row + 0.5f);
|
||||||
|
|
||||||
|
ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||||
|
origin.x += pad;
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_NavHighlight, style.color_line);
|
||||||
|
ImGui::SetWindowFontScale(1.50f);
|
||||||
|
|
||||||
|
const int function_rows_start = std::max(0, max_row - 2);
|
||||||
|
|
||||||
|
/* ─────────────── 2. draw every key ───────────────────────── */
|
||||||
|
for (const KeyEntry& key : layout) {
|
||||||
|
/* position & size */
|
||||||
|
float x = origin.x + key.col * (cell_w + style.key_spacing);
|
||||||
|
float y = origin.y + key.row * (cell_h + style.key_spacing);
|
||||||
|
float w = key.colspan * cell_w + (key.colspan - 1) * style.key_spacing;
|
||||||
|
float h = key.rowspan * cell_h + (key.rowspan - 1) * style.key_spacing;
|
||||||
|
ImVec2 pos(x, y), size(w, h);
|
||||||
|
|
||||||
|
/* ------------ background colour decision --------------- */
|
||||||
|
const bool in_function_rows = (key.row >= function_rows_start);
|
||||||
|
const bool is_done_enter = (key.keycode == 0x0D);
|
||||||
|
|
||||||
|
ImU32 bg_color;
|
||||||
|
if (is_done_enter) {
|
||||||
|
bg_color = style.color_special; // always wins
|
||||||
|
} else if (in_function_rows) {
|
||||||
|
bg_color = style.color_button_function; // bottom two rows
|
||||||
|
} else if ((mode == KeyboardMode::Symbols1 || mode == KeyboardMode::Symbols2) &&
|
||||||
|
style.use_button_symbol_color) {
|
||||||
|
bg_color = style.color_button_symbol; // symbol tint
|
||||||
|
} else {
|
||||||
|
bg_color = style.color_button_default; // normal default
|
||||||
|
}
|
||||||
|
|
||||||
|
/* label */
|
||||||
|
std::string label = (key.label && key.label[0]) ? key.label : " ";
|
||||||
|
|
||||||
|
/* ---------- ImGui button ---------- */
|
||||||
|
ImGui::PushID(&key);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, style.color_text);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, bg_color);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||||
|
IM_COL32((bg_color >> IM_COL32_R_SHIFT & 0xFF) * 220 / 255,
|
||||||
|
(bg_color >> IM_COL32_G_SHIFT & 0xFF) * 220 / 255,
|
||||||
|
(bg_color >> IM_COL32_B_SHIFT & 0xFF) * 220 / 255,
|
||||||
|
(bg_color >> IM_COL32_A_SHIFT & 0xFF)));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||||
|
IM_COL32((bg_color >> IM_COL32_R_SHIFT & 0xFF) * 180 / 255,
|
||||||
|
(bg_color >> IM_COL32_G_SHIFT & 0xFF) * 180 / 255,
|
||||||
|
(bg_color >> IM_COL32_B_SHIFT & 0xFF) * 180 / 255,
|
||||||
|
(bg_color >> IM_COL32_A_SHIFT & 0xFF)));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||||
|
|
||||||
|
if (key.allow_repeat)
|
||||||
|
ImGui::PushButtonRepeat(true);
|
||||||
|
|
||||||
|
ImGui::SetCursorScreenPos(pos);
|
||||||
|
bool pressed = ImGui::Button(label.c_str(), size); // Down + repeats
|
||||||
|
|
||||||
|
// ——— use ImGui’s built‑in hover highlight ———
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetItemCurrentNavFocus();
|
||||||
|
ImGui::KeepNavHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.allow_repeat)
|
||||||
|
ImGui::PopButtonRepeat();
|
||||||
|
|
||||||
|
/* ---------- event generation ---------- */
|
||||||
|
if (on_key_event) {
|
||||||
|
if (ImGui::IsItemActivated()) {
|
||||||
|
VirtualKeyEvent ev{VirtualKeyEventType::Down, &key};
|
||||||
|
on_key_event(&ev);
|
||||||
|
} else if (pressed && key.allow_repeat) {
|
||||||
|
VirtualKeyEvent ev{VirtualKeyEventType::Repeat, &key};
|
||||||
|
on_key_event(&ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsItemDeactivated()) {
|
||||||
|
VirtualKeyEvent ev{VirtualKeyEventType::Up, &key};
|
||||||
|
on_key_event(&ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
ImGui::PopStyleVar(2);
|
||||||
|
ImGui::PopStyleColor(4);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetWindowFontScale(1.0f);
|
||||||
|
ImGui::PopStyleColor(); // NavHighlight
|
||||||
|
ImGui::EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the correct layout via GetKeyboardLayout() then calls RenderKeyboardLayout().
|
||||||
|
*/
|
||||||
|
void DrawVirtualKeyboard(KeyboardMode kb_mode, OrbisImeType ime_type, ShiftState shift_state,
|
||||||
|
u64 language, void (*on_key_event)(const VirtualKeyEvent*),
|
||||||
|
const KeyboardStyle& style) {
|
||||||
|
const std::vector<KeyEntry>* layout =
|
||||||
|
GetKeyboardLayout(ime_type, kb_mode, shift_state, language);
|
||||||
|
if (!layout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RenderKeyboardLayout(*layout, kb_mode, on_key_event, style);
|
||||||
|
}
|
89
src/core/libraries/ime/ime_keyboard_ui.h
Normal file
89
src/core/libraries/ime/ime_keyboard_ui.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "core/libraries/ime/ime_common.h"
|
||||||
|
#include "core/libraries/ime/ime_keyboard_layouts.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KeyboardMode: which layout we show (letters, accents, symbols, etc.)
|
||||||
|
*/
|
||||||
|
enum class KeyboardMode { Letters, AccentLetters, Symbols1, Symbols2 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We handle raw key "Down" or "Up" events from an on-screen keyboard.
|
||||||
|
*/
|
||||||
|
enum class VirtualKeyEventType { Down, Up, Repeat };
|
||||||
|
|
||||||
|
struct VirtualKeyEvent {
|
||||||
|
VirtualKeyEventType type;
|
||||||
|
const KeyEntry* key;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShiftState : u8 {
|
||||||
|
None = 0, // lowercase
|
||||||
|
Shift = 1, // temporary uppercase
|
||||||
|
CapsLock = 2 // full uppercase
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct holds all visual parameters for the on-screen keyboard,
|
||||||
|
* including layout size, spacing, and button colors.
|
||||||
|
*
|
||||||
|
* If extended parameters are present, it override these defaults
|
||||||
|
* in IME code. Then pass the result to DrawVirtualKeyboard(...).
|
||||||
|
*/
|
||||||
|
struct KeyboardStyle {
|
||||||
|
float layout_width = 500.0f;
|
||||||
|
float layout_height = 300.0f;
|
||||||
|
float key_spacing = 5.0f;
|
||||||
|
|
||||||
|
// For text, lines, etc.
|
||||||
|
ImU32 color_text = IM_COL32(225, 225, 225, 255);
|
||||||
|
ImU32 color_line = IM_COL32(88, 88, 88, 255);
|
||||||
|
|
||||||
|
// Button colors
|
||||||
|
ImU32 color_button_default = IM_COL32(35, 35, 35, 255);
|
||||||
|
ImU32 color_button_function = IM_COL32(50, 50, 50, 255);
|
||||||
|
ImU32 color_special = IM_COL32(0, 140, 200, 255);
|
||||||
|
|
||||||
|
// If you're on a symbols layout, you may want to color them differently.
|
||||||
|
bool use_button_symbol_color = false;
|
||||||
|
ImU32 color_button_symbol = IM_COL32(60, 60, 60, 255);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely remove one UTF-8 glyph from the end of 'buffer'.
|
||||||
|
*/
|
||||||
|
void Utf8SafeBackspace(char* buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the appropriate layout (vector of KeyEntry) for the given
|
||||||
|
* OrbisImeType, KeyboardMode, ShiftState, and language bitmask.
|
||||||
|
*/
|
||||||
|
const std::vector<KeyEntry>* GetKeyboardLayout(OrbisImeType type, KeyboardMode mode,
|
||||||
|
ShiftState shift, u64 language);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a given layout using the style logic:
|
||||||
|
* - If 'mode' is a symbols layout (Symbols1 or Symbols2) AND style.use_button_symbol_color == true,
|
||||||
|
* then normal character keys are drawn with style.color_button_symbol
|
||||||
|
* - Function keys => style.color_button_function
|
||||||
|
* - The "Done" or "Enter" key (keycode 0x0D) => style.color_special
|
||||||
|
* - Otherwise => style.color_button_default
|
||||||
|
*
|
||||||
|
* We call on_key_event(...) with VirtualKeyEventType::Down/Up when the user clicks or releases a
|
||||||
|
* key.
|
||||||
|
*/
|
||||||
|
void RenderKeyboardLayout(const std::vector<KeyEntry>& layout, KeyboardMode mode,
|
||||||
|
void (*on_key_event)(const VirtualKeyEvent*), const KeyboardStyle& style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks the correct layout from GetKeyboardLayout() for the given
|
||||||
|
* kb_mode, shift_state, etc., then calls RenderKeyboardLayout().
|
||||||
|
*/
|
||||||
|
void DrawVirtualKeyboard(KeyboardMode kb_mode, OrbisImeType ime_type, ShiftState shift_state,
|
||||||
|
u64 language, void (*on_key_event)(const VirtualKeyEvent*),
|
||||||
|
const KeyboardStyle& style);
|
|
@ -88,4 +88,23 @@ static void DrawCenteredText(const char* text, const char* text_end = nullptr,
|
||||||
SetCursorPos(pos + content);
|
SetCursorPos(pos + content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void DrawCaretForInputText(const char* text, int caret_index, ImVec2 input_pos,
|
||||||
|
float padding_x = 4.0f, float padding_y = 3.0f) {
|
||||||
|
ImVec2 text_size = ImGui::CalcTextSize(text, text + caret_index);
|
||||||
|
float caret_x = input_pos.x + padding_x + text_size.x;
|
||||||
|
float caret_y = input_pos.y + padding_y;
|
||||||
|
|
||||||
|
float caret_height = ImGui::GetTextLineHeight();
|
||||||
|
|
||||||
|
// Optional: make caret blink like ImGui does
|
||||||
|
float time = ImGui::GetTime();
|
||||||
|
bool visible = (fmodf(time, 1.2f) < 0.8f);
|
||||||
|
if (!visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui::GetWindowDrawList()->AddLine(ImVec2(caret_x, caret_y),
|
||||||
|
ImVec2(caret_x, caret_y + caret_height),
|
||||||
|
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ImGui
|
} // namespace ImGui
|
||||||
|
|
|
@ -69,14 +69,51 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
|
||||||
rb.AddRanges(io.Fonts->GetGlyphRangesKorean());
|
rb.AddRanges(io.Fonts->GetGlyphRangesKorean());
|
||||||
rb.AddRanges(io.Fonts->GetGlyphRangesJapanese());
|
rb.AddRanges(io.Fonts->GetGlyphRangesJapanese());
|
||||||
rb.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
|
rb.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
|
||||||
|
// For keyboard
|
||||||
|
rb.AddChar(U'×');
|
||||||
|
rb.AddChar(U'○');
|
||||||
|
rb.AddChar(U'△');
|
||||||
|
rb.AddChar(U'□');
|
||||||
|
rb.AddChar(U'↑');
|
||||||
|
rb.AddChar(U'↓');
|
||||||
|
rb.AddChar(U'←');
|
||||||
|
rb.AddChar(U'→');
|
||||||
|
rb.AddChar(U'⊗');
|
||||||
|
rb.AddChar(U'⮾');
|
||||||
|
rb.AddChar(U'╳');
|
||||||
|
rb.AddChar(U'◀');
|
||||||
|
rb.AddChar(U'▲');
|
||||||
|
rb.AddChar(U'▶');
|
||||||
|
rb.AddChar(U'▼');
|
||||||
|
rb.AddChar(U'⇧');
|
||||||
|
rb.AddChar(U'⬆');
|
||||||
|
rb.AddChar(U'⇦');
|
||||||
|
rb.AddChar(U'€');
|
||||||
|
rb.AddChar(U'₩');
|
||||||
|
rb.AddChar(U'“');
|
||||||
|
rb.AddChar(U'”');
|
||||||
|
rb.AddChar(U'„');
|
||||||
|
rb.AddChar(U'‼');
|
||||||
|
rb.AddChar(U'¿');
|
||||||
|
rb.AddChar(U'⁇');
|
||||||
|
rb.AddChar(U'‹');
|
||||||
|
rb.AddChar(U'›');
|
||||||
|
rb.AddChar(U'’');
|
||||||
|
rb.AddChar(U'‘');
|
||||||
|
rb.AddChar(U'‛');
|
||||||
|
rb.AddChar(U'‚');
|
||||||
|
rb.AddChar(U'№');
|
||||||
|
|
||||||
ImVector<ImWchar> ranges{};
|
ImVector<ImWchar> ranges{};
|
||||||
rb.BuildRanges(&ranges);
|
rb.BuildRanges(&ranges);
|
||||||
ImFontConfig font_cfg{};
|
ImFontConfig font_cfg{};
|
||||||
font_cfg.OversampleH = 2;
|
font_cfg.OversampleH = 2;
|
||||||
font_cfg.OversampleV = 1;
|
font_cfg.OversampleV = 1;
|
||||||
|
font_cfg.MergeMode = false;
|
||||||
io.FontDefault = io.Fonts->AddFontFromMemoryCompressedTTF(
|
io.FontDefault = io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
imgui_font_notosansjp_regular_compressed_data,
|
imgui_font_notosansjp_regular_compressed_data,
|
||||||
imgui_font_notosansjp_regular_compressed_size, 16.0f, &font_cfg, ranges.Data);
|
imgui_font_notosansjp_regular_compressed_size, 16.0f, &font_cfg, ranges.Data);
|
||||||
|
font_cfg.MergeMode = true;
|
||||||
io.Fonts->AddFontFromMemoryCompressedTTF(imgui_font_proggyvector_regular_compressed_data,
|
io.Fonts->AddFontFromMemoryCompressedTTF(imgui_font_proggyvector_regular_compressed_data,
|
||||||
imgui_font_proggyvector_regular_compressed_size,
|
imgui_font_proggyvector_regular_compressed_size,
|
||||||
16.0f);
|
16.0f);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue