ime composition support (#589)

This commit is contained in:
Michael 2024-12-28 07:45:34 -06:00 committed by GitHub
parent c8faa22072
commit ba037771bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 95 additions and 7 deletions

View file

@ -66,6 +66,10 @@ void keyboard_on_text_input(char* text) {
djui_interactable_on_text_input(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) { static void keyboard_add_binds(int mask, unsigned int *scancode) {
for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) { for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) {
if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) { if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) {

View file

@ -42,6 +42,7 @@ bool keyboard_on_key_down(int scancode);
bool keyboard_on_key_up(int scancode); bool keyboard_on_key_up(int scancode);
void keyboard_on_all_keys_up(void); void keyboard_on_all_keys_up(void);
void keyboard_on_text_input(char* text); void keyboard_on_text_input(char* text);
void keyboard_on_text_editing(char* text, int cursorPos);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -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 // In case the game crashed before the game window opened
if (!gGfxInited) { if (!gGfxInited) {
gfx_init(&WAPI, &RAPI, TITLE); 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(); if (!gGameInited) djui_unicode_init();

View file

@ -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) { void djui_chat_box_toggle(void) {
if (gDjuiChatBox == NULL) { return; } if (gDjuiChatBox == NULL) { return; }
if (!gDjuiChatBoxFocus) { sDjuiChatBoxClearText = true; } 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_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_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_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; chatBox->chatInput = chatInput;
gDjuiChatBox = chatBox; gDjuiChatBox = chatBox;

View file

@ -397,6 +397,31 @@ void djui_inputbox_on_text_input(struct DjuiBase *base, char* text) {
inputbox->selection[1] = inputbox->selection[0]; inputbox->selection[1] = inputbox->selection[0];
sCursorBlink = 0; sCursorBlink = 0;
djui_inputbox_on_change(inputbox); 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) { 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; 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 // render only cursor when there is no selection width
if (selection[0] == selection[1]) { if (selection[0] == selection[1]) {
if (sCursorBlink < DJUI_INPUTBOX_MID_BLINK && djui_interactable_is_input_focus(&inputbox->base)) { 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); create_dl_scale_matrix(DJUI_MTX_NOPUSH, DJUI_INPUTBOX_CURSOR_WIDTH, 0.8f, 1.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
gSPDisplayList(gDisplayListHead++, dl_djui_simple_rect); gSPDisplayList(gDisplayListHead++, dl_djui_simple_rect);
@ -547,13 +583,23 @@ static bool djui_inputbox_render(struct DjuiBase* base) {
u16 selection[2] = { 0 }; u16 selection[2] = { 0 };
selection[0] = fmin(inputbox->selection[0], inputbox->selection[1]); selection[0] = fmin(inputbox->selection[0], inputbox->selection[1]);
selection[1] = fmax(inputbox->selection[0], inputbox->selection[1]); selection[1] = fmax(inputbox->selection[0], inputbox->selection[1]);
// render text // render text
char* c = inputbox->buffer; char* c = inputbox->buffer;
f32 drawX = inputbox->viewX; f32 drawX = inputbox->viewX;
f32 additionalShift = 0; f32 additionalShift = 0;
bool wasInsideSelection = false; bool wasInsideSelection = false;
for (u16 i = 0; i < inputbox->bufferSize; i++) { 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; } if (*c == '\0') { break; }
// deal with seleciton color // 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_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_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_input(base, djui_inputbox_on_text_input);
djui_interactable_hook_text_editing(base, djui_inputbox_on_text_editing);
djui_inputbox_update_style(base); djui_inputbox_update_style(base);

View file

@ -11,6 +11,8 @@ struct DjuiInputbox {
struct DjuiColor textColor; struct DjuiColor textColor;
void (*on_enter_press)(struct DjuiInputbox*); void (*on_enter_press)(struct DjuiInputbox*);
void (*on_escape_press)(struct DjuiInputbox*); void (*on_escape_press)(struct DjuiInputbox*);
char* imeBuffer;
u16 imePos;
}; };
void djui_inputbox_on_focus_begin(UNUSED struct DjuiBase* base); 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); 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_key_up(struct DjuiBase* base, int scancode);
void djui_inputbox_on_text_input(struct DjuiBase *base, char* text); 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); struct DjuiInputbox* djui_inputbox_create(struct DjuiBase* parent, u16 bufferSize);

View file

@ -328,6 +328,13 @@ void djui_interactable_on_text_input(char* text) {
gInteractableFocus->interactable->on_text_input(gInteractableFocus, 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) { void djui_interactable_update_pad(void) {
OSContPad* pad = &gInteractablePad; OSContPad* pad = &gInteractablePad;
@ -521,6 +528,12 @@ void djui_interactable_hook_text_input(struct DjuiBase *base,
interactable->on_text_input = on_text_input; 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 djui_interactable_hook_enabled_change(struct DjuiBase *base,
void (*on_enabled_change)(struct DjuiBase*)) { void (*on_enabled_change)(struct DjuiBase*)) {
struct DjuiInteractable *interactable = base->interactable; struct DjuiInteractable *interactable = base->interactable;

View file

@ -42,6 +42,7 @@ struct DjuiInteractable {
bool (*on_key_down)(struct DjuiBase*, int scancode); bool (*on_key_down)(struct DjuiBase*, int scancode);
void (*on_key_up)(struct DjuiBase*, int scancode); void (*on_key_up)(struct DjuiBase*, int scancode);
void (*on_text_input)(struct DjuiBase*, char* text); void (*on_text_input)(struct DjuiBase*, char* text);
void (*on_text_editing)(struct DjuiBase*, char* text, int cursorPos);
void (*on_enabled_change)(struct DjuiBase*); 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); bool djui_interactable_on_key_down(int scancode);
void djui_interactable_on_key_up(int scancode); void djui_interactable_on_key_up(int scancode);
void djui_interactable_on_text_input(char *text); void djui_interactable_on_text_input(char *text);
void djui_interactable_on_text_editing(char* text, int cursorPos);
void djui_interactable_update(void); 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 djui_interactable_hook_text_input(struct DjuiBase* base,
void (*on_text_input)(struct DjuiBase*, char*)); 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 djui_interactable_hook_enabled_change(struct DjuiBase *base,
void (*on_enabled_change)(struct DjuiBase*)); void (*on_enabled_change)(struct DjuiBase*));

View file

@ -65,6 +65,7 @@ static kb_callback_t kb_key_down = NULL;
static kb_callback_t kb_key_up = NULL; static kb_callback_t kb_key_up = NULL;
static void (*kb_all_keys_up)(void) = NULL; static void (*kb_all_keys_up)(void) = NULL;
static void (*kb_text_input)(char*) = 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) #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: case SDL_TEXTINPUT:
kb_text_input(event.text.text); kb_text_input(event.text.text);
break; break;
case SDL_TEXTEDITING: //IME composition
kb_text_editing(event.edit.text,event.edit.start);
break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
gfx_sdl_onkeydown(event.key.keysym.scancode); gfx_sdl_onkeydown(event.key.keysym.scancode);
break; 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_down = on_key_down;
kb_key_up = on_key_up; kb_key_up = on_key_up;
kb_all_keys_up = on_all_keys_up; kb_all_keys_up = on_all_keys_up;
kb_text_input = on_text_input; kb_text_input = on_text_input;
kb_text_editing = on_text_editing;
} }
static bool gfx_sdl_start_frame(void) { static bool gfx_sdl_start_frame(void) {

View file

@ -13,7 +13,8 @@ typedef bool (*kb_callback_t)(int code);
struct GfxWindowManagerAPI { struct GfxWindowManagerAPI {
void (*init)(const char *window_title); 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 (*main_loop)(void (*run_one_game_iter)(void));
void (*get_dimensions)(uint32_t *width, uint32_t *height); void (*get_dimensions)(uint32_t *width, uint32_t *height);
void (*handle_events)(void); void (*handle_events)(void);

View file

@ -439,7 +439,8 @@ int main(int argc, char *argv[]) {
// create the window almost straight away // create the window almost straight away
if (!gGfxInited) { if (!gGfxInited) {
gfx_init(&WAPI, &RAPI, TITLE); 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 // render the rom setup screen