diff --git a/CMakeLists.txt b/CMakeLists.txt index b66ef9a..809d188 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,7 @@ elseif (GTK2_FOUND) link_libraries(${Intl_LIBRARIES}) endif() else() - set(SRC_MENU menu_null.c) + set(SRC_MENU menu_null.c sdl_input.c) endif() # Graphics diff --git a/src/menu.h b/src/menu.h index 8c8a338..4a1e66f 100644 --- a/src/menu.h +++ b/src/menu.h @@ -26,7 +26,7 @@ #include "portab.h" /* 文字列入力のパラメータ */ -typedef struct { +typedef struct inputstring_param { char *title; char *oldstring; const char *newstring; diff --git a/src/menu_null.c b/src/menu_null.c index 04dc504..7358dab 100644 --- a/src/menu_null.c +++ b/src/menu_null.c @@ -26,6 +26,7 @@ #include "portab.h" #include "menu.h" +#include "sdl_core.h" void menu_open(void) { return; @@ -41,8 +42,7 @@ boolean menu_inputstring(INPUTSTRING_PARAM *p) { } boolean menu_inputstring2(INPUTSTRING_PARAM *p) { - p->newstring = p->oldstring; - return TRUE; + return sdl_inputString(p); } boolean menu_inputnumber(INPUTNUM_PARAM *p) { diff --git a/src/sdl_core.h b/src/sdl_core.h index df42096..e472e9a 100644 --- a/src/sdl_core.h +++ b/src/sdl_core.h @@ -29,6 +29,8 @@ #include "ags.h" #include "cursor.h" +struct inputstring_param; + /* 初期化関係 */ extern int sdl_Initilize(void); extern void sdl_Remove(void); @@ -102,6 +104,7 @@ extern void sdl_mainIteration(); extern boolean RawKeyInfo[]; extern void sdl_sleep(int msec); extern void sdl_wait_vsync(); +extern boolean sdl_inputString(struct inputstring_param *); /* 初期化関係 */ #define GraphicsInitilize() sdl_Initilize() diff --git a/src/sdl_event.c b/src/sdl_event.c index 886c594..e36f4c1 100644 --- a/src/sdl_event.c +++ b/src/sdl_event.c @@ -96,6 +96,10 @@ static void sdl_getEvent(void) { while (SDL_PollEvent(&e)) { had_input = true; + + if (sdl_custom_event_handler && sdl_custom_event_handler(&e)) + continue; + switch (e.type) { #ifndef __EMSCRIPTEN__ case SDL_QUIT: diff --git a/src/sdl_input.c b/src/sdl_input.c new file mode 100644 index 0000000..06b9eaa --- /dev/null +++ b/src/sdl_input.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +#include "config.h" + +#include +#include + +#include "sdl_core.h" +#include "sdl_private.h" +#include "menu.h" +#include "nact.h" + +#define XMARGIN 3 +#define YMARGIN 2 + +struct EditorState { + INPUTSTRING_PARAM *params; + SDL_Rect rect; + char *text; + char composingText[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; + int cursor; + boolean done; +}; +static struct EditorState *input; + +static void redraw() { + const int bgcolor = nact->msg.WinBackgroundColor; + const int fgcolor = nact->msg.MsgFontColor; + + ags_fillRectangle(input->rect.x, input->rect.y, input->rect.w, input->rect.h, bgcolor); + + SDL_Rect r = { + .x = input->rect.x + XMARGIN, + .y = input->rect.y + YMARGIN, + .w = input->rect.w - XMARGIN * 2, + .h = input->rect.h - YMARGIN * 2, + }; + + if (*input->text) { + int w = sdl_drawString(r.x, r.y, input->text, fgcolor); + r.x += w; + r.w -= w; + } + + if (*input->composingText) { + int w = sdl_drawString(r.x, r.y, input->composingText, fgcolor); + ags_fillRectangle(r.x, r.y + r.h, w, 2, fgcolor); // underline + } + + // FIXME: This doesn't work with propotional fonts! + int cursor_x = r.x + r.h * input->cursor; + ags_fillRectangle(cursor_x, r.y, 2, r.h, fgcolor); // cursor + + // Dib geometry -> window geometry + r.x -= nact->sys_view_area.x; + r.y -= nact->sys_view_area.y; + SDL_SetTextInputRect(&r); + + ags_updateArea(input->rect.x, input->rect.y, input->rect.w, input->rect.h); +} + +static boolean handle_event(const SDL_Event *e) { + switch (e->type) { + case SDL_KEYDOWN: + if (*input->composingText) + break; // Let the IME handle this event. + switch (e->key.keysym.sym) { + case SDLK_RETURN: + input->params->newstring = input->text; + input->done = TRUE; + return TRUE; + case SDLK_ESCAPE: + input->params->newstring = NULL; + input->done = TRUE; + return TRUE; + case SDLK_BACKSPACE: + { + char *p = input->text + strlen(input->text) - 1; + while (p >= input->text && UTF8_TRAIL_BYTE(*p)) + p--; + if (p >= input->text) + *p = '\0'; + redraw(); + } + return TRUE; + } + break; + + case SDL_TEXTINPUT: + { + int chars = input->params->max; + for (const char *p = input->text; *p; p = advance_char(p, UTF8)) + chars--; + const char *end = e->text.text; + while (chars > 0 && *end) { + end = advance_char(end, UTF8); + chars--; + } + strncat(input->text, e->text.text, end - e->text.text); + + input->composingText[0] = '\0'; + input->cursor = 0; + redraw(); + } + return TRUE; + + case SDL_TEXTEDITING: + strncpy(input->composingText, e->edit.text, SDL_TEXTEDITINGEVENT_TEXT_SIZE); + input->cursor = e->edit.start; + redraw(); + return TRUE; + } + return FALSE; +} + +boolean sdl_inputString(INPUTSTRING_PARAM *p) { + static char *buf; + free(buf); + buf = malloc(p->max * MAX_UTF8_BYTES_PAR_CHAR + 1); + + struct EditorState s = { + .params = p, + .rect = { + .x = p->x + nact->sys_view_area.x, // window geometry -> dib geometry + .y = p->y + nact->sys_view_area.y, + .w = p->h * p->max + XMARGIN * 2, + .h = p->h + YMARGIN * 2 + }, + .text = buf, + }; + strcpy(s.text, p->oldstring); + input = &s; + + SDL_StartTextInput(); + + void *saved_region = ags_saveRegion(s.rect.x, s.rect.y, s.rect.w, s.rect.h); + ags_setFont(FONT_GOTHIC, p->h); + redraw(); + + sdl_custom_event_handler = handle_event; + while (!input->done) { + sdl_getKeyInfo(); // message pump + nact->callback(); + sdl_wait_vsync(); + } + SDL_StopTextInput(); + sdl_custom_event_handler = NULL; + + ags_restoreRegion(saved_region, s.rect.x, s.rect.y); + ags_updateArea(s.rect.x, s.rect.y, s.rect.w, s.rect.h); + + input = NULL; + + return TRUE; +} diff --git a/src/sdl_private.h b/src/sdl_private.h index 799bc73..90035dc 100644 --- a/src/sdl_private.h +++ b/src/sdl_private.h @@ -59,6 +59,7 @@ struct sdl_private_data { int winoffset_x; /* draw offset in Window x */ int winoffset_y; /* y */ + boolean (*custom_event_handler)(const SDL_Event *); }; extern void sdl_cursor_init(void); @@ -88,6 +89,7 @@ extern struct sdl_private_data *sdl_videodev; #define sdl_fs_on (sdl_videodev->fs_on) #define winoffset_x (sdl_videodev->winoffset_x) #define winoffset_y (sdl_videodev->winoffset_y) +#define sdl_custom_event_handler (sdl_videodev->custom_event_handler) #define setRect(r,xx,yy,ww,hh) (r).x=(xx),(r).y=(yy),(r).w=(ww),(r).h=(hh) #define setOffset(s,x,y) (s->pixels) + (x) * (s->format->BytesPerPixel) + (y) * s->pitch