diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index d859c95bc..18a3db407 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -66,6 +66,10 @@ void keyboard_on_text_input(char* text) { djui_interactable_on_text_input(text); } +void keyboard_on_text_editing(char* text, int cursorPos) { + djui_interactable_on_text_editing(text, cursorPos); +} + static void keyboard_add_binds(int mask, unsigned int *scancode) { for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) { if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) { diff --git a/src/pc/controller/controller_keyboard.h b/src/pc/controller/controller_keyboard.h index 8f52d38b6..b1dabcc40 100644 --- a/src/pc/controller/controller_keyboard.h +++ b/src/pc/controller/controller_keyboard.h @@ -42,6 +42,7 @@ bool keyboard_on_key_down(int scancode); bool keyboard_on_key_up(int scancode); void keyboard_on_all_keys_up(void); void keyboard_on_text_input(char* text); +void keyboard_on_text_editing(char* text, int cursorPos); #ifdef __cplusplus } diff --git a/src/pc/crash_handler.c b/src/pc/crash_handler.c index 44a72b16d..fea9f02c8 100644 --- a/src/pc/crash_handler.c +++ b/src/pc/crash_handler.c @@ -651,7 +651,8 @@ static void crash_handler(const int signalNum, siginfo_t *info, UNUSED ucontext_ // In case the game crashed before the game window opened if (!gGfxInited) { gfx_init(&WAPI, &RAPI, TITLE); - WAPI.set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, keyboard_on_text_input); + WAPI.set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, + keyboard_on_text_input, keyboard_on_text_editing); } if (!gGameInited) djui_unicode_init(); diff --git a/src/pc/djui/djui_chat_box.c b/src/pc/djui/djui_chat_box.c index b3ef836f3..3a430602f 100644 --- a/src/pc/djui/djui_chat_box.c +++ b/src/pc/djui/djui_chat_box.c @@ -498,6 +498,10 @@ static void djui_chat_box_input_on_text_input(struct DjuiBase *base, char* text) } } +static void djui_chat_box_input_on_text_editing(struct DjuiBase *base, char* text, int cursorPos) { + djui_inputbox_on_text_editing(base, text, cursorPos); +} + void djui_chat_box_toggle(void) { if (gDjuiChatBox == NULL) { return; } if (!gDjuiChatBoxFocus) { sDjuiChatBoxClearText = true; } @@ -550,6 +554,7 @@ struct DjuiChatBox* djui_chat_box_create(void) { djui_base_set_alignment(ciBase, DJUI_HALIGN_LEFT, DJUI_VALIGN_BOTTOM); djui_interactable_hook_key(&chatInput->base, djui_chat_box_input_on_key_down, djui_inputbox_on_key_up); djui_interactable_hook_text_input(&chatInput->base, djui_chat_box_input_on_text_input); + djui_interactable_hook_text_editing(&chatInput->base, djui_chat_box_input_on_text_editing); chatBox->chatInput = chatInput; gDjuiChatBox = chatBox; diff --git a/src/pc/djui/djui_inputbox.c b/src/pc/djui/djui_inputbox.c index 92b8e7ef4..408960717 100644 --- a/src/pc/djui/djui_inputbox.c +++ b/src/pc/djui/djui_inputbox.c @@ -397,6 +397,31 @@ void djui_inputbox_on_text_input(struct DjuiBase *base, char* text) { inputbox->selection[1] = inputbox->selection[0]; sCursorBlink = 0; djui_inputbox_on_change(inputbox); + + inputbox->imePos = 0; + if (inputbox->imeBuffer != NULL) { + free(inputbox->imeBuffer); + inputbox->imeBuffer = NULL; + } +} + +void djui_inputbox_on_text_editing(struct DjuiBase *base, char* text, int cursorPos) { + struct DjuiInputbox *inputbox = (struct DjuiInputbox *) base; + inputbox->imePos = (u16)cursorPos; + + if (inputbox->imeBuffer != NULL) free(inputbox->imeBuffer); + + if (*text == '\0') { + inputbox->imeBuffer = NULL; + } + else { + size_t size = strlen(text); + char* copy = malloc(size + 1); + strcpy(copy,text); + inputbox->imeBuffer = copy; + } + + djui_inputbox_on_change(inputbox); } static void djui_inputbox_render_char(struct DjuiInputbox* inputbox, char* c, f32* drawX, f32* additionalShift) { @@ -444,11 +469,22 @@ static void djui_inputbox_render_selection(struct DjuiInputbox* inputbox) { } sCursorBlink = (sCursorBlink + 1) % DJUI_INPUTBOX_MAX_BLINK; - + + f32 renderX = x; + + u16 imePos = inputbox->imePos; + if (imePos != 0) { + char* ime = inputbox->imeBuffer; + for (u16 i = 0; i < imePos; i++) { + renderX += font->char_width(ime); + ime = djui_unicode_next_char(ime); + } + } + // render only cursor when there is no selection width if (selection[0] == selection[1]) { if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK && djui_interactable_is_input_focus(&inputbox->base)) { - create_dl_translation_matrix(DJUI_MTX_PUSH, x - DJUI_INPUTBOX_CURSOR_WIDTH / 2.0f, -0.1f, 0); + create_dl_translation_matrix(DJUI_MTX_PUSH, renderX - DJUI_INPUTBOX_CURSOR_WIDTH / 2.0f, -0.1f, 0); create_dl_scale_matrix(DJUI_MTX_NOPUSH, DJUI_INPUTBOX_CURSOR_WIDTH, 0.8f, 1.0f); gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); gSPDisplayList(gDisplayListHead++, dl_djui_simple_rect); @@ -547,13 +583,23 @@ static bool djui_inputbox_render(struct DjuiBase* base) { u16 selection[2] = { 0 }; selection[0] = fmin(inputbox->selection[0], inputbox->selection[1]); selection[1] = fmax(inputbox->selection[0], inputbox->selection[1]); - + // render text char* c = inputbox->buffer; f32 drawX = inputbox->viewX; f32 additionalShift = 0; bool wasInsideSelection = false; for (u16 i = 0; i < inputbox->bufferSize; i++) { + + //render composition text + if (selection[0] == i && inputbox->imeBuffer != NULL) { + char *ime = inputbox->imeBuffer; + while (*ime != '\0') { + djui_inputbox_render_char(inputbox, ime, &drawX, &additionalShift); + ime = djui_unicode_next_char(ime); + } + } + if (*c == '\0') { break; } // deal with seleciton color @@ -598,6 +644,7 @@ struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSiz djui_interactable_hook_key(base, djui_inputbox_on_key_down, djui_inputbox_on_key_up); djui_interactable_hook_focus(base, djui_inputbox_on_focus_begin, NULL, djui_inputbox_on_focus_end); djui_interactable_hook_text_input(base, djui_inputbox_on_text_input); + djui_interactable_hook_text_editing(base, djui_inputbox_on_text_editing); djui_inputbox_update_style(base); diff --git a/src/pc/djui/djui_inputbox.h b/src/pc/djui/djui_inputbox.h index 5bbcba620..a590d0f6e 100644 --- a/src/pc/djui/djui_inputbox.h +++ b/src/pc/djui/djui_inputbox.h @@ -11,6 +11,8 @@ struct DjuiInputbox { struct DjuiColor textColor; void (*on_enter_press)(struct DjuiInputbox*); void (*on_escape_press)(struct DjuiInputbox*); + char* imeBuffer; + u16 imePos; }; void djui_inputbox_on_focus_begin(UNUSED struct DjuiBase* base); @@ -25,5 +27,6 @@ void djui_inputbox_hook_escape_press(struct DjuiInputbox* inputbox, void (*on_es bool djui_inputbox_on_key_down(struct DjuiBase* base, int scancode); void djui_inputbox_on_key_up(struct DjuiBase* base, int scancode); void djui_inputbox_on_text_input(struct DjuiBase *base, char* text); +void djui_inputbox_on_text_editing(struct DjuiBase *base, char* text, int cursorPos); struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSize); diff --git a/src/pc/djui/djui_interactable.c b/src/pc/djui/djui_interactable.c index 1a9e0e310..b950a3cc0 100644 --- a/src/pc/djui/djui_interactable.c +++ b/src/pc/djui/djui_interactable.c @@ -328,6 +328,13 @@ void djui_interactable_on_text_input(char* text) { gInteractableFocus->interactable->on_text_input(gInteractableFocus, text); } +void djui_interactable_on_text_editing(char* text, int cursorPos) { + if (gInteractableFocus == NULL) { return; } + if (gInteractableFocus->interactable == NULL) { return; } + if (gInteractableFocus->interactable->on_text_editing == NULL) { return; } + gInteractableFocus->interactable->on_text_editing(gInteractableFocus, text, cursorPos); +} + void djui_interactable_update_pad(void) { OSContPad* pad = &gInteractablePad; @@ -521,6 +528,12 @@ void djui_interactable_hook_text_input(struct DjuiBase *base, interactable->on_text_input = on_text_input; } +void djui_interactable_hook_text_editing(struct DjuiBase* base, + void (*on_text_editing)(struct DjuiBase*, char*, int)) { + struct DjuiInteractable *interactable = base->interactable; + interactable->on_text_editing = on_text_editing; +} + void djui_interactable_hook_enabled_change(struct DjuiBase *base, void (*on_enabled_change)(struct DjuiBase*)) { struct DjuiInteractable *interactable = base->interactable; diff --git a/src/pc/djui/djui_interactable.h b/src/pc/djui/djui_interactable.h index 9496e45a0..64d1bddc6 100644 --- a/src/pc/djui/djui_interactable.h +++ b/src/pc/djui/djui_interactable.h @@ -42,6 +42,7 @@ struct DjuiInteractable { bool (*on_key_down)(struct DjuiBase*, int scancode); void (*on_key_up)(struct DjuiBase*, int scancode); void (*on_text_input)(struct DjuiBase*, char* text); + void (*on_text_editing)(struct DjuiBase*, char* text, int cursorPos); void (*on_enabled_change)(struct DjuiBase*); }; @@ -62,6 +63,7 @@ bool djui_interactable_is_input_focus(struct DjuiBase* base); bool djui_interactable_on_key_down(int scancode); void djui_interactable_on_key_up(int scancode); void djui_interactable_on_text_input(char *text); +void djui_interactable_on_text_editing(char* text, int cursorPos); void djui_interactable_update(void); @@ -95,6 +97,9 @@ void djui_interactable_hook_key(struct DjuiBase* base, void djui_interactable_hook_text_input(struct DjuiBase* base, void (*on_text_input)(struct DjuiBase*, char*)); +void djui_interactable_hook_text_editing(struct DjuiBase* base, + void (*on_text_editing)(struct DjuiBase*, char*, int)); + void djui_interactable_hook_enabled_change(struct DjuiBase *base, void (*on_enabled_change)(struct DjuiBase*)); diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index c980c560b..bc2a4a8ce 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -65,6 +65,7 @@ static kb_callback_t kb_key_down = NULL; static kb_callback_t kb_key_up = NULL; static void (*kb_all_keys_up)(void) = NULL; static void (*kb_text_input)(char*) = NULL; +static void (*kb_text_editing)(char*, int) = NULL; #define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) @@ -211,6 +212,9 @@ static void gfx_sdl_handle_events(void) { case SDL_TEXTINPUT: kb_text_input(event.text.text); break; + case SDL_TEXTEDITING: //IME composition + kb_text_editing(event.edit.text,event.edit.start); + break; case SDL_KEYDOWN: gfx_sdl_onkeydown(event.key.keysym.scancode); break; @@ -249,11 +253,14 @@ static void gfx_sdl_handle_events(void) { } } -static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void), void (*on_text_input)(char*)) { +static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, +void (*on_all_keys_up)(void), void (*on_text_input)(char*), void (*on_text_editing)(char*, int)) +{ kb_key_down = on_key_down; kb_key_up = on_key_up; kb_all_keys_up = on_all_keys_up; kb_text_input = on_text_input; + kb_text_editing = on_text_editing; } static bool gfx_sdl_start_frame(void) { diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 57204a859..5f1fa5d8a 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -13,7 +13,8 @@ typedef bool (*kb_callback_t)(int code); struct GfxWindowManagerAPI { void (*init)(const char *window_title); - void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void), void (*on_text_input)(char*)); + void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void), + void (*on_text_input)(char*), void (*on_text_editing)(char*, int)); void (*main_loop)(void (*run_one_game_iter)(void)); void (*get_dimensions)(uint32_t *width, uint32_t *height); void (*handle_events)(void); diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index b0818773f..6ea06a348 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -439,7 +439,8 @@ int main(int argc, char *argv[]) { // create the window almost straight away if (!gGfxInited) { gfx_init(&WAPI, &RAPI, TITLE); - WAPI.set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, keyboard_on_text_input); + WAPI.set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up, + keyboard_on_text_input, keyboard_on_text_editing); } // render the rom setup screen