diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 89aba7cbf..3c8bcfddf 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -1,8 +1,5 @@ -// ime_dialog_ui.cpp -// ---------------------------------------------------------- -// Full implementation of IME dialog UI with on‑screen keyboard -// (all original logic intact, bugs fixed). -// ---------------------------------------------------------- +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include #include @@ -52,13 +49,14 @@ static void KeyboardCallbackBridge(const VirtualKeyEvent* evt) { *─────────────────────────────────────────────────────────────*/ ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, const OrbisImeParamExtended* extended) { - if (!param) + if (!param) { return; + } /* basic param copy */ user_id = param->user_id; is_multi_line = True(param->option & OrbisImeDialogOption::Multiline); - is_numeric = (param->type == OrbisImeType::Number); + is_numeric = param->type == OrbisImeType::Number; type = param->type; enter_label = param->enter_label; text_filter = param->filter; @@ -262,7 +260,7 @@ ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept other.status = nullptr; other.result = nullptr; - if (state && status && *status == OrbisImeDialogStatus::Running) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); ImeDialogUi::g_activeImeDialogUi = this; } @@ -276,12 +274,11 @@ ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { status = other.status; result = other.result; first_render = other.first_render; - other.state = nullptr; other.status = nullptr; other.result = nullptr; - if (state && status && *status == OrbisImeDialogStatus::Running) { + if (state && *status == OrbisImeDialogStatus::Running) { AddLayer(this); ImeDialogUi::g_activeImeDialogUi = this; } @@ -329,6 +326,7 @@ void ImeDialogUi::Draw() { if (Begin("IME Dialog##ImeDialog", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { DrawPrettyBackground(); + /* ---------- title ---------- */ if (!state->title.empty()) { SetCursorPosX(20.0f); @@ -351,41 +349,47 @@ void ImeDialogUi::Draw() { DrawVirtualKeyboardSection(); /* ---------- OK / Cancel buttons ---------- */ - /* { - SetCursorPosY(GetCursorPosY() + 10.0f); - const char* ok_lbl = "OK##ImeDialogOK"; - switch (state->enter_label) { - case OrbisImeEnterLabel::Go: - ok_lbl = "Go##ImeDialogOK"; - break; - case OrbisImeEnterLabel::Search: - ok_lbl = "Search##ImeDialogOK"; - break; - case OrbisImeEnterLabel::Send: - ok_lbl = "Send##ImeDialogOK"; - break; - default: - break; - } + /* + SetCursorPosY(GetCursorPosY() + 10.0f); - float spacing = 10.0f; - float total_w = BUTTON_SIZE.x * 2 + spacing; - float x_start = (window_size.x - total_w) / 2.0f; - SetCursorPosX(x_start); + const char* button_text; - if (Button(ok_lbl, BUTTON_SIZE) || - (!state->is_multi_line && IsKeyPressed(ImGuiKey_Enter))) { - *status = OrbisImeDialogStatus::Finished; - result->endstatus = OrbisImeDialogEndStatus::Ok; - } + 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; + } - SameLine(0.0f, spacing); + 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; - if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { - *status = OrbisImeDialogStatus::Finished; - result->endstatus = OrbisImeDialogEndStatus::UserCanceled; - } - }*/ + 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(); } @@ -397,65 +401,131 @@ void ImeDialogUi::Draw() { * helper draw functions (unchanged) *─────────────────────────────────────────────────────────────*/ void ImeDialogUi::DrawInputText() { - ImVec2 size(GetWindowWidth() - 40.0f, 0.0f); + ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; SetCursorPosX(20.0f); - if (first_render) + if (first_render) { SetKeyboardFocusHere(); - - const char* ph = state->placeholder.empty() ? nullptr : state->placeholder.data(); - if (InputTextEx("##ImeDialogInput", ph, state->current_text.begin(), state->max_text_length, - size, ImGuiInputTextFlags_CallbackCharFilter, InputTextCallback, this)) + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter, + InputTextCallback, this)) { state->input_changed = true; + } } void ImeDialogUi::DrawMultiLineInputText() { - ImVec2 size(GetWindowWidth() - 40.0f, 200.0f); + ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f}; SetCursorPosX(20.0f); ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter | static_cast(ImGuiInputTextFlags_Multiline); - if (first_render) + if (first_render) { SetKeyboardFocusHere(); - - const char* ph = state->placeholder.empty() ? nullptr : state->placeholder.data(); - if (InputTextEx("##ImeDialogInput", ph, state->current_text.begin(), state->max_text_length, - size, flags, InputTextCallback, this)) + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, flags, InputTextCallback, this)) { state->input_changed = true; + } } int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { ImeDialogUi* ui = static_cast(data->UserData); ASSERT(ui); - /* numeric filter */ + // Should we filter punctuation? if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') && - data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') + data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') { return 1; + } - if (!ui->state->keyboard_filter) + if (!ui->state->keyboard_filter) { return 0; + } - char* ev_char = reinterpret_cast(&data->EventChar); + // ImGui encodes ImWchar32 as multi-byte UTF-8 characters + char* event_char = reinterpret_cast(&data->EventChar); - OrbisImeKeycode src{ + // Call the keyboard filter + OrbisImeKeycode src_keycode = { .keycode = 0, .character = 0, - .status = 1, - .type = OrbisImeKeyboardType::ENGLISH_US, + .status = 1, // ??? 1 = key pressed, 0 = key released + .type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use + // the current language?) .user_id = ui->state->user_id, .resource_id = 0, .timestamp = 0, }; - if (!ui->state->ConvertUTF8ToOrbis(ev_char, 4, &src.character, 1)) + if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); return 0; - src.keycode = src.character; + } + src_keycode.keycode = src_keycode.character; // TODO set this to the correct value + + u16 out_keycode; + u32 out_status; + + ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status); + + // TODO. set the keycode - u16 out_code; - u32 out_stat; - ui->state->CallKeyboardFilter(&src, &out_code, &out_stat); return 0; } +/* 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; + + 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_Button, IM_COL32(0, 0, 0, 255)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(0, 0, 0, 255)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(0, 0, 0, 255)); + ImGui::PushStyleColor(ImGuiCol_Text, kb_style.color_text); + + if (ImGui::Button("predict", ImVec2(width - bar_h - 3 * pad, bar_h))) { + } + 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; + } + ImGui::PopStyleColor(4); + SetCursorPosX(0.0f); + SetCursorPosY(GetCursorPosY() + 5.0f); +} + /*─────────────────────────────────────────────────────────────* * helper draw functions (new) *─────────────────────────────────────────────────────────────*/ @@ -516,56 +586,4 @@ void ImeDialogUi::OnVirtualKeyEvent(const VirtualKeyEvent* evt) { } /* Up is available if you need it later; currently ignored */ } - -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; - - 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_Button, IM_COL32(0, 0, 0, 255)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(0, 0, 0, 255)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(0, 0, 0, 255)); - ImGui::PushStyleColor(ImGuiCol_Text, kb_style.color_text); - - if (ImGui::Button("predict", ImVec2(width - bar_h - 3 * pad, bar_h))) { - } - 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; - } - ImGui::PopStyleColor(4); - SetCursorPosX(0.0f); - SetCursorPosY(GetCursorPosY() + 5.0f); -} - } // namespace Libraries::ImeDialog diff --git a/src/core/libraries/ime/ime_dialog_ui.h b/src/core/libraries/ime/ime_dialog_ui.h index 0984602ce..b262207ca 100644 --- a/src/core/libraries/ime/ime_dialog_ui.h +++ b/src/core/libraries/ime/ime_dialog_ui.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + #pragma once #include // for strncpy / memcpy @@ -10,8 +13,6 @@ #include "ime_keyboard_ui.h" #include "imgui/imgui_layer.h" -#include - namespace Libraries::ImeDialog { // Forward declaration so we can befriend it @@ -35,11 +36,10 @@ class ImeDialogState final { OrbisImeExtKeyboardFilter keyboard_filter{}; u32 max_text_length{}; char16_t* text_buffer{}; - std::vector title; std::vector placeholder; - // One UTF‑8 code‑point may take up to 4 bytes + // A character can hold up to 4 bytes in UTF-8 Common::CString current_text; // Optional custom keyboard style (from extended params) @@ -49,11 +49,13 @@ class ImeDialogState final { public: /*──────────────── constructors / rule‑of‑five ────────────────*/ ImeDialogState(const OrbisImeDialogParam* param = nullptr, - const OrbisImeParamExtended* ext = nullptr); - ImeDialogState(const ImeDialogState&) = delete; - ImeDialogState(ImeDialogState&&) noexcept; - ImeDialogState& operator=(ImeDialogState&&); + const OrbisImeParamExtended* extended = nullptr); + ImeDialogState(const ImeDialogState& other) = delete; + ImeDialogState(ImeDialogState&& other) noexcept; + ImeDialogState& operator=(ImeDialogState&& other); + bool CopyTextToOrbisBuffer(); + bool CallTextFilter(); /*──────────────────── public read helpers ───────────────────*/ bool IsMultiLine() const { return is_multi_line; @@ -103,17 +105,15 @@ public: input_changed = true; } - /*──────────────────────── IME support ───────────────────────*/ - bool CopyTextToOrbisBuffer(); - bool CallTextFilter(); + private: bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, - std::size_t utf8_text_len); - bool ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, char16_t* orbis_text, - std::size_t orbis_text_len); + std::size_t native_text_len); + bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len); }; //--------------------------------------------------------------------- @@ -136,9 +136,9 @@ public: explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, OrbisImeDialogResult* result = nullptr); ~ImeDialogUi() override; - ImeDialogUi(const ImeDialogUi&) = delete; - ImeDialogUi(ImeDialogUi&&) noexcept; - ImeDialogUi& operator=(ImeDialogUi&&); + ImeDialogUi(const ImeDialogUi& other) = delete; + ImeDialogUi(ImeDialogUi&& other) noexcept; + ImeDialogUi& operator=(ImeDialogUi&& other); /*────────── main draw ───────────*/ void Draw() override; @@ -149,8 +149,10 @@ public: private: /*── helpers ─*/ void Free(); + void DrawInputText(); void DrawMultiLineInputText(); + static int InputTextCallback(ImGuiInputTextCallbackData* data); /*── keyboard section ─*/ @@ -158,6 +160,18 @@ private: ShiftState shift_state = ShiftState::None; u64 kb_language = 0; KeyboardStyle kb_style; + /* KeyboardStyle kb_style{ + .layout_width = 500.0f, + .layout_height = 250.0f, + .key_spacing = 5.0f, + .color_text = IM_COL32(225,225,225,255), + .color_line = IM_COL32( 88, 88, 88,255), + .color_button_default = IM_COL32( 35, 35, 35,255), + .color_button_function = IM_COL32( 50, 50, 50,255), + .color_special = IM_COL32( 0,140,200,255), + .use_button_symbol_color= false, + .color_button_symbol = IM_COL32( 60, 60, 60,255), + };*/ void DrawVirtualKeyboardSection(); void DrawPredictionBarAnCancelButton(); diff --git a/src/core/libraries/ime/ime_keyboard_layouts.cpp b/src/core/libraries/ime/ime_keyboard_layouts.cpp index 7d009119a..7642fcc22 100644 --- a/src/core/libraries/ime/ime_keyboard_layouts.cpp +++ b/src/core/libraries/ime/ime_keyboard_layouts.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include - #include "ime_keyboard_layouts.h" int c16rtomb(char* out, char16_t ch) { diff --git a/src/core/libraries/ime/ime_keyboard_layouts.h b/src/core/libraries/ime/ime_keyboard_layouts.h index 73e83ec82..b6eb8575c 100644 --- a/src/core/libraries/ime/ime_keyboard_layouts.h +++ b/src/core/libraries/ime/ime_keyboard_layouts.h @@ -20,7 +20,7 @@ struct KeyEntry { u8 rowspan; const char* label; const char* controller_hint; - ImGuiNavInput bound_buttons[2]; + ImGuiKey bound_buttons[2]; bool allow_repeat{false}; }; @@ -33,22 +33,23 @@ extern const std::vector kLayoutEnAccentLettersLowercase; extern const std::vector kLayoutEnSymbols1; extern const std::vector kLayoutEnSymbols2; -constexpr ImGuiNavInput None = ImGuiNavInput_COUNT; -constexpr auto L1 = ImGuiNavInput_FocusPrev; -constexpr auto R1 = ImGuiNavInput_FocusNext; -constexpr auto L2 = ImGuiNavInput_TweakSlow; -constexpr auto R2 = ImGuiNavInput_TweakFast; -constexpr auto L3 = ImGuiNavInput_DpadLeft; // adjust if needed -constexpr auto R3 = ImGuiNavInput_DpadRight; // adjust if needed -constexpr auto Up = ImGuiNavInput_DpadUp; -constexpr auto Down = ImGuiNavInput_DpadDown; -constexpr auto Left = ImGuiNavInput_DpadLeft; -constexpr auto Right = ImGuiNavInput_DpadRight; -constexpr auto Cross = ImGuiNavInput_Activate; -constexpr auto Circle = ImGuiNavInput_Menu; -constexpr auto Square = ImGuiNavInput_Cancel; -constexpr auto Triangle = ImGuiNavInput_Input; -constexpr auto TouchPad = ImGuiNavInput_Menu; // reuse if needed +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; diff --git a/src/core/libraries/ime/ime_keyboard_ui.cpp b/src/core/libraries/ime/ime_keyboard_ui.cpp index 927df0feb..68674488c 100644 --- a/src/core/libraries/ime/ime_keyboard_ui.cpp +++ b/src/core/libraries/ime/ime_keyboard_ui.cpp @@ -7,6 +7,8 @@ #include "ime_dialog.h" #include "ime_keyboard_layouts.h" #include "ime_keyboard_ui.h" +#include "ime_ui.h" // for ImeState + using namespace ImGui; /** diff --git a/src/core/libraries/ime/ime_keyboard_ui.h b/src/core/libraries/ime/ime_keyboard_ui.h index c2507c346..9e8fdef86 100644 --- a/src/core/libraries/ime/ime_keyboard_ui.h +++ b/src/core/libraries/ime/ime_keyboard_ui.h @@ -3,8 +3,12 @@ #include #include #include - -#include "core/libraries/ime/ime_keyboard_layouts.h" +#include "core/libraries/ime/ime.h" +#include "core/libraries/ime/ime_common.h" +#include "core/libraries/ime/ime_error.h" +#include "core/libraries/ime/ime_ui.h" +#include "core/libraries/pad/pad.h" +#include "ime_keyboard_layouts.h" /** * KeyboardMode: which layout we show (letters, accents, symbols, etc.)