From bdd2b4d4c9c15791c7f588b9e7a4d0d3ba81899e Mon Sep 17 00:00:00 2001 From: w1naenator Date: Wed, 30 Apr 2025 01:47:43 +0300 Subject: [PATCH] Initial caret draw/control --- src/core/libraries/ime/ime_dialog_ui.cpp | 27 +++++++++-- src/core/libraries/ime/ime_dialog_ui.h | 46 ++++++++++++++++--- src/core/libraries/ime/ime_keyboard_layouts.h | 6 ++- src/imgui/imgui_std.h | 19 ++++++++ 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/src/core/libraries/ime/ime_dialog_ui.cpp b/src/core/libraries/ime/ime_dialog_ui.cpp index 698f531f0..92a1a630b 100644 --- a/src/core/libraries/ime/ime_dialog_ui.cpp +++ b/src/core/libraries/ime/ime_dialog_ui.cpp @@ -379,6 +379,16 @@ void ImeDialogUi::DrawInputText() { InputTextCallback, this)) { 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(); @@ -601,14 +611,25 @@ void ImeDialogUi::OnVirtualKeyEvent(const VirtualKeyEvent* evt) { case KeyType::Character: { char utf8[8]{}; int n = c16rtomb(utf8, key->character); - if (n > 0) - state->AppendUtf8(utf8, (size_t)n); + 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->BackspaceUtf8(); + state->BackspaceUtf8AtCaret(); break; // Backspace case 0x0D: *status = OrbisImeDialogStatus::Finished; // Enter diff --git a/src/core/libraries/ime/ime_dialog_ui.h b/src/core/libraries/ime/ime_dialog_ui.h index 516a30785..e1a3c0b40 100644 --- a/src/core/libraries/ime/ime_dialog_ui.h +++ b/src/core/libraries/ime/ime_dialog_ui.h @@ -45,6 +45,7 @@ class ImeDialogState final { // Optional custom keyboard style (from extended params) bool has_custom_style = false; KeyboardStyle custom_kb_style{}; + int caret_index = 0; public: /*──────────────── constructors / rule‑of‑five ────────────────*/ @@ -91,15 +92,23 @@ public: input_changed = true; } - // Append raw UTF‑8 sequence of length 'len' - void AppendUtf8(const char* utf8, std::size_t len) { + void InsertUtf8AtCaret(const char* utf8, std::size_t len) { if (!utf8 || len == 0) return; - std::size_t old = std::strlen(current_text.begin()); - if (old + len >= current_text.capacity()) - return; // full: silently ignore - std::memcpy(current_text.begin() + old, utf8, len); - current_text[old + len] = '\0'; + + 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; } @@ -109,6 +118,29 @@ public: 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(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: bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); diff --git a/src/core/libraries/ime/ime_keyboard_layouts.h b/src/core/libraries/ime/ime_keyboard_layouts.h index 045b06503..4e5cdc18a 100644 --- a/src/core/libraries/ime/ime_keyboard_layouts.h +++ b/src/core/libraries/ime/ime_keyboard_layouts.h @@ -56,4 +56,8 @@ 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; \ No newline at end of file +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; diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index 743702657..82770258e 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -88,4 +88,23 @@ static void DrawCenteredText(const char* text, const char* text_end = nullptr, 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