From 830479f50cc0b03d6368c3fd8d3c2e9c7149cae3 Mon Sep 17 00:00:00 2001 From: Pk11 Date: Thu, 19 Aug 2021 22:26:57 -0500 Subject: [PATCH] Fix multibyte letters in keyboard and input prompt --- arm9/source/common/swkbd.c | 77 ++++++++++++++++++--------- arm9/source/common/ui.c | 106 +++++++++++++++++++++++++++++-------- arm9/source/common/ui.h | 3 ++ 3 files changed, 139 insertions(+), 47 deletions(-) diff --git a/arm9/source/common/swkbd.c b/arm9/source/common/swkbd.c index a01622a..6cf79b9 100644 --- a/arm9/source/common/swkbd.c +++ b/arm9/source/common/swkbd.c @@ -119,34 +119,50 @@ static void DrawKeyBoard(TouchBox* swkbd, const u32 uppercase) { } static void DrawTextBox(const TouchBox* txtbox, const char* inputstr, const u32 cursor, u32* scroll) { - const u32 input_shown = (txtbox->w / FONT_WIDTH_EXT) - 2; + const u32 input_shown_length = (txtbox->w / FONT_WIDTH_EXT) - 2; const u32 inputstr_size = strlen(inputstr); // we rely on a zero terminated string const u16 x = txtbox->x; const u16 y = txtbox->y; // fix scroll - if (cursor < *scroll) *scroll = cursor; - else if (cursor - *scroll > input_shown) *scroll = cursor - input_shown; + if (cursor < *scroll) { + *scroll = cursor; + } else { + int scroll_adjust = -input_shown_length; + for (u32 i = *scroll; i < cursor; i++) { + if (i >= inputstr_size || (inputstr[i] & 0xC0) != 0x80) scroll_adjust++; + } + + for (int i = 0; i < scroll_adjust; i++) + *scroll += *scroll >= inputstr_size ? 1 : GetCharSize(inputstr + *scroll); + } + + u32 input_shown_size = 0; + for (u32 i = 0; i < input_shown_length || (*scroll + input_shown_size < inputstr_size && (inputstr[*scroll + input_shown_size] & 0xC0) == 0x80); input_shown_size++) { + if (*scroll + input_shown_size >= inputstr_size || (inputstr[*scroll + input_shown_size] & 0xC0) != 0x80) i++; + } // draw input string - DrawStringF(BOT_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%-*.*s%c", + DrawStringF(BOT_SCREEN, x, y, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%c", (*scroll) ? '<' : '|', - (inputstr_size > input_shown) ? input_shown : inputstr_size, - (inputstr_size > input_shown) ? input_shown : inputstr_size, + input_shown_size, + input_shown_size, (*scroll > inputstr_size) ? "" : inputstr + *scroll, - (inputstr_size > input_shown) ? 0 : input_shown - inputstr_size, - (inputstr_size > input_shown) ? 0 : input_shown - inputstr_size, - "", - (inputstr_size - (s32) *scroll > input_shown) ? '>' : '|' + (inputstr_size - (s32) *scroll > input_shown_size) ? '>' : '|' ); // draw cursor + u16 cpos = 0; + for (u16 i = *scroll; i < cursor; i++) { + if (i >= inputstr_size || (inputstr[i] & 0xC0) != 0x80) cpos++; + } + DrawStringF(BOT_SCREEN, x-(FONT_WIDTH_EXT/2), y+10, COLOR_STD_FONT, COLOR_STD_BG, "%-*.*s^%-*.*s", - 1 + cursor - *scroll, - 1 + cursor - *scroll, + 1 + cpos, + 1 + cpos, "", - input_shown - (cursor - *scroll), - input_shown - (cursor - *scroll), + input_shown_length - cpos, + input_shown_length - cpos, "" ); } @@ -163,8 +179,17 @@ static void MoveTextBoxCursor(const TouchBox* txtbox, const char* inputstr, cons const TouchBox* tb = TouchBoxGet(&id, x, y, txtbox, 0); if (id == KEY_TXTBOX) { u16 x_tb = x - tb->x; - u16 cpos = (x_tb < (FONT_WIDTH_EXT/2)) ? 0 : (x_tb - (FONT_WIDTH_EXT/2)) / FONT_WIDTH_EXT; - u32 cursor_next = *scroll + ((cpos <= input_shown) ? cpos : input_shown); + + const u32 inputstr_size = strlen(inputstr); + const u16 cpos_x = (x_tb < (FONT_WIDTH_EXT/2)) ? 0 : (x_tb - (FONT_WIDTH_EXT/2)) / FONT_WIDTH_EXT; + u16 cpos_length = 0; + u16 cpos_size = 0; + while ((cpos_length < cpos_x && cpos_length < input_shown) || (*scroll + cpos_size < inputstr_size && (inputstr[*scroll + cpos_size] & 0xC0) == 0x80)) { + if (*scroll + cpos_size >= inputstr_size || (inputstr[*scroll + cpos_size] & 0xC0) != 0x80) cpos_length++; + cpos_size++; + } + + u32 cursor_next = *scroll + cpos_size; // move cursor to position pointed to if (*cursor != cursor_next) { if (cursor_next < max_size) *cursor = cursor_next; @@ -173,10 +198,10 @@ static void MoveTextBoxCursor(const TouchBox* txtbox, const char* inputstr, cons } // move beyound visible field if (timer_msec(timer) >= scroll_cooldown) { - if ((cpos == 0) && (*scroll > 0)) - (*scroll)--; - else if ((cpos >= input_shown) && (*cursor < (max_size-1))) - (*scroll)++; + if ((cpos_length == 0) && (*scroll > 0)) + *scroll -= GetPrevCharSize(inputstr + *scroll); + else if ((cpos_length >= input_shown) && (*cursor < (max_size-1))) + *scroll += GetCharSize(inputstr + *scroll); } } } @@ -286,16 +311,18 @@ bool ShowKeyboard(char* inputstr, const u32 max_size, const char *format, ...) { break; } else if (key == KEY_BKSPC) { if (cursor) { + int size = GetPrevCharSize(inputstr + cursor); if (cursor <= inputstr_size) { - memmove(inputstr + cursor - 1, inputstr + cursor, inputstr_size - cursor + 1); - inputstr_size--; + memmove(inputstr + cursor - size, inputstr + cursor, inputstr_size - cursor + size); + inputstr_size -= size; } - cursor--; + cursor -= size; } } else if (key == KEY_LEFT) { - if (cursor) cursor--; + if (cursor) cursor -= GetPrevCharSize(inputstr + cursor); } else if (key == KEY_RIGHT) { - if (cursor < (max_size-1)) cursor++; + int size = cursor > inputstr_size ? 1 : GetCharSize(inputstr + cursor); + if (cursor + size < max_size) cursor += size; } else if (key == KEY_ALPHA) { swkbd = swkbd_alphabet; } else if (key == KEY_SPECIAL) { diff --git a/arm9/source/common/ui.c b/arm9/source/common/ui.c index 0900106..d8fb7cb 100644 --- a/arm9/source/common/ui.c +++ b/arm9/source/common/ui.c @@ -476,6 +476,24 @@ u32 GetDrawStringHeight(const char* str) { return height; } +u32 GetCharSize(const char* str) { + const char *start = str; + do { + str++; + } while ((*str & 0xC0) == 0x80); + + return str - start; +} + +u32 GetPrevCharSize(const char* str) { + const char *start = str; + do { + str--; + } while ((*str & 0xC0) == 0x80); + + return start - str; +} + u32 GetDrawStringWidth(const char* str) { u32 width = 0; char* old_lf = (char*) str; @@ -994,7 +1012,7 @@ u32 ShowHotkeyPrompt(u32 n, const char** options, const u32* keys, const char *f bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alphabet, const char *format, va_list va) { const u32 alphabet_size = strnlen(alphabet, 256); - const u32 input_shown = 22; + const u32 input_shown_length = 22; const u32 fast_scroll = 4; const u64 aprv_delay = 128; @@ -1033,25 +1051,43 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha while (true) { u32 inputstr_size = strnlen(inputstr, max_size - 1); - if (cursor_s < scroll) scroll = cursor_s; - else if (cursor_s - scroll >= input_shown) scroll = cursor_s - input_shown + 1; - while (scroll && (inputstr_size - scroll < input_shown)) scroll--; - DrawStringF(MAIN_SCREEN, x, y + str_height - 76, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%c%-*.*s\n%-*.*s^%-*.*s", + + if (cursor_s < scroll) { + scroll = cursor_s; + } else { + int scroll_adjust = -input_shown_length; + for (u32 i = scroll; i < cursor_s; i++) { + if (i >= inputstr_size || (inputstr[i] & 0xC0) != 0x80) scroll_adjust++; + } + + for (int i = 0; i <= scroll_adjust; i++) + scroll += scroll >= inputstr_size ? 1 : GetCharSize(inputstr + scroll); + } + + u32 input_shown_size = 0; + for (u32 i = 0; i < input_shown_length || (scroll + input_shown_size < inputstr_size && (inputstr[scroll + input_shown_size] & 0xC0) == 0x80); input_shown_size++) { + if (scroll + input_shown_size >= inputstr_size || (inputstr[scroll + input_shown_size] & 0xC0) != 0x80) i++; + } + + u16 cpos = 0; + for (u16 i = scroll; i < cursor_s; i++) { + if (i >= inputstr_size || (inputstr[i] & 0xC0) != 0x80) cpos++; + } + + DrawStringF(MAIN_SCREEN, x, y + str_height - 76, COLOR_STD_FONT, COLOR_STD_BG, "%c%-*.*s%c\n%-*.*s^%-*.*s", (scroll) ? '<' : '|', - (inputstr_size > input_shown) ? input_shown : inputstr_size, - (inputstr_size > input_shown) ? input_shown : inputstr_size, - inputstr + scroll, - (inputstr_size - scroll > input_shown) ? '>' : '|', - (inputstr_size > input_shown) ? 0 : input_shown - inputstr_size, - (inputstr_size > input_shown) ? 0 : input_shown - inputstr_size, + input_shown_size, + input_shown_size, + (scroll > inputstr_size) ? "" : inputstr + scroll, + (inputstr_size - (s32) scroll > input_shown_size) ? '>' : '|', + 1 + cpos, + 1 + cpos, "", - 1 + cursor_s - scroll, - 1 + cursor_s - scroll, - "", - input_shown - (cursor_s - scroll), - input_shown - (cursor_s - scroll), + input_shown_length - cpos, + input_shown_length - cpos, "" ); + if (cursor_a < 0) { for (cursor_a = alphabet_size - 1; (cursor_a > 0) && (alphabet[cursor_a] != inputstr[cursor_s]); cursor_a--); } @@ -1091,11 +1127,26 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha } } else if (pad_state & BUTTON_X) { if (resize && (inputstr_size > resize)) { - char* inputfrom = inputstr + cursor_s - (cursor_s % resize) + resize; - char* inputto = inputstr + cursor_s - (cursor_s % resize); + u32 char_index = 0; + for(u32 i = 0; i < cursor_s; i++) { + if (i >= inputstr_size || (inputstr[i] & 0xC0) != 0x80) char_index++; + } + + u32 to_index = char_index - (char_index % resize); + u32 from_index = to_index + resize; + + char* inputto = inputstr + cursor_s; + for (u32 i = 0; i < char_index - to_index; i++) { + inputto -= GetPrevCharSize(inputto); + } + char* inputfrom = inputto; + for (u32 i = 0; i < from_index - to_index; i++) { + inputfrom += GetCharSize(inputfrom); + } + memmove(inputto, inputfrom, max_size - (inputfrom - inputstr)); - inputstr_size -= resize; - while (cursor_s >= inputstr_size) + inputstr_size -= inputfrom - inputto; + while (cursor_s >= inputstr_size || (inputstr[cursor_s] & 0xC0) == 0x80) cursor_s--; cursor_a = -1; } else if (resize == 1) { @@ -1112,18 +1163,29 @@ bool ShowInputPrompt(char* inputstr, u32 max_size, u32 resize, const char* alpha cursor_a = 0; } } else if (pad_state & BUTTON_UP) { + int size = GetCharSize(inputstr + cursor_s); + if(size > 1) { + memmove(inputstr + cursor_s, inputstr + cursor_s + size - 1, inputstr_size - cursor_s + size - 1); + } + cursor_a += (pad_state & BUTTON_R1) ? fast_scroll : 1; cursor_a = cursor_a % alphabet_size; inputstr[cursor_s] = alphabet[cursor_a]; } else if (pad_state & BUTTON_DOWN) { + int size = GetCharSize(inputstr + cursor_s); + if(size > 1) { + memmove(inputstr + cursor_s, inputstr + cursor_s + size - 1, inputstr_size - cursor_s + size - 1); + } + cursor_a -= (pad_state & BUTTON_R1) ? fast_scroll : 1; if (cursor_a < 0) cursor_a = alphabet_size + cursor_a; inputstr[cursor_s] = alphabet[cursor_a]; } else if (pad_state & BUTTON_LEFT) { - if (cursor_s > 0) cursor_s--; + if (cursor_s > 0) cursor_s -= GetPrevCharSize(inputstr + cursor_s); cursor_a = -1; } else if (pad_state & BUTTON_RIGHT) { - if (cursor_s < max_size - 2) cursor_s++; + int size = cursor_s > inputstr_size ? 1 : GetCharSize(inputstr + cursor_s); + if (cursor_s + size < max_size - 1) cursor_s += size; if (cursor_s >= inputstr_size) { memset(inputstr + cursor_s, alphabet[0], resize); inputstr[cursor_s + resize] = '\0'; diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index bfcd379..071ff99 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -66,6 +66,9 @@ void DrawString(u16 *screen, const char *str, int x, int y, u32 color, u32 bgcol void DrawStringF(u16 *screen, int x, int y, u32 color, u32 bgcolor, const char *format, ...); void DrawStringCenter(u16 *screen, u32 color, u32 bgcolor, const char *format, ...); +u32 GetCharSize(const char* str); +u32 GetPrevCharSize(const char* str); + u32 GetDrawStringHeight(const char* str); u32 GetDrawStringWidth(const char* str); u32 GetFontWidth(void);