diff --git a/include/mgba-util/gui.h b/include/mgba-util/gui.h index ec2a00b9e..05f31d4e1 100644 --- a/include/mgba-util/gui.h +++ b/include/mgba-util/gui.h @@ -48,6 +48,17 @@ enum GUIKeyboardStatus { GUI_KEYBOARD_CANCEL, }; +enum GUIKeyFunction { + GUI_KEYFUNC_INPUT_DATA = 0, + GUI_KEYFUNC_CHANGE_KB, + GUI_KEYFUNC_SHIFT_KB, + GUI_KEYFUNC_BACKSPACE, + GUI_KEYFUNC_ENTER, + GUI_KEYFUNC_CANCEL, + GUI_KEYFUNC_LEFT, + GUI_KEYFUNC_RIGHT, +}; + enum { BATTERY_EMPTY = 0, BATTERY_LOW = 25, @@ -72,6 +83,21 @@ struct GUIKeyboardParams { bool multiline; }; +struct GUIKey { + const char* name; + const void* data; + int width; + enum GUIKeyFunction function; +}; + +struct GUIKeyboard { + struct { + int offset; + struct GUIKey* keys; + } rows[5]; + int width; +}; + struct GUIParams { unsigned width; unsigned height; diff --git a/include/mgba-util/gui/font.h b/include/mgba-util/gui/font.h index 5ad318e65..a4e74d89a 100644 --- a/include/mgba-util/gui/font.h +++ b/include/mgba-util/gui/font.h @@ -84,6 +84,18 @@ enum GUIIcon { GUI_ICON_9SLICE_CAP_SWW, GUI_ICON_9SLICE_CAP_SSE, GUI_ICON_9SLICE_CAP_SEE, + GUI_ICON_9SLICE_FILL_ONLY_NW, + GUI_ICON_9SLICE_FILL_ONLY_N, + GUI_ICON_9SLICE_FILL_ONLY_NE, + GUI_ICON_9SLICE_FILL_ONLY_W, + GUI_ICON_9SLICE_FILL_ONLY_C, + GUI_ICON_9SLICE_FILL_ONLY_E, + GUI_ICON_9SLICE_FILL_ONLY_SW, + GUI_ICON_9SLICE_FILL_ONLY_S, + GUI_ICON_9SLICE_FILL_ONLY_SE, + GUI_ICON_BACKSPACE, + GUI_ICON_KBD_SHIFT, + GUI_ICON_CAPSLOCK, GUI_ICON_MAX, }; @@ -109,6 +121,7 @@ enum GUI9SliceStyle { GUI_9SLICE_FILLED, GUI_9SLICE_EMPTY, GUI_9SLICE_EMPTY_CAPPED, + GUI_9SLICE_FILL_ONLY, }; unsigned GUIFontHeight(const struct GUIFont*); diff --git a/res/icons.png b/res/icons.png index 1b75cddd9..acb509141 100644 Binary files a/res/icons.png and b/res/icons.png differ diff --git a/res/icons2x.png b/res/icons2x.png index c4066f4ef..91e833531 100644 Binary files a/res/icons2x.png and b/res/icons2x.png differ diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 3e435f6e2..587f096e3 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -89,6 +89,8 @@ static void _drawEnd(void); static uint32_t _pollInput(const struct mInputMap*); static enum GUICursorState _pollCursor(unsigned* x, unsigned* y); static void _guiPrepare(void); +static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard); +static struct GUIParams* params; // XXX static void _setup(struct mGUIRunner* runner); static void _gameLoaded(struct mGUIRunner* runner); @@ -355,6 +357,7 @@ int main(int argc, char* argv[]) { _pollInput, _pollCursor, 0, _guiPrepare, 0, + _keyboardRun, }, .keySources = (struct GUIInputKeys[]) { { @@ -600,6 +603,9 @@ int main(int argc, char* argv[]) { mGUIInit(&runner, "wii"); reconfigureScreen(&runner); + // XXX + params = &runner.params; + // Make sure screen is properly initialized by drawing a blank frame _drawStart(); _drawEnd(); @@ -844,6 +850,432 @@ void _guiPrepare(void) { _reproj2(vmode->fbWidth * guiScale * wAdjust, vmode->efbHeight * guiScale * hAdjust); } +static const struct GUIKeyboard qwertyLower; +static const struct GUIKeyboard qwertyUpper; +static const struct GUIKeyboard symbols; + +static const struct GUIKeyboard qwertyLower = { + .rows = { + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "1", "1" }, + { "2", "2" }, + { "3", "3" }, + { "4", "4" }, + { "5", "5" }, + { "6", "6" }, + { "7", "7" }, + { "8", "8" }, + { "9", "9" }, + { "0", "0" }, + { "-", "-" }, + { "⌫", NULL, 2, GUI_KEYFUNC_BACKSPACE }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "q", "q" }, + { "w", "w" }, + { "e", "e" }, + { "r", "r" }, + { "t", "t" }, + { "y", "y" }, + { "u", "u" }, + { "i", "i" }, + { "o", "o" }, + { "p", "p" }, + { "[", "[" }, + { "]", "]" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "a", "a" }, + { "s", "s" }, + { "d", "d" }, + { "f", "f" }, + { "g", "g" }, + { "h", "h" }, + { "j", "j" }, + { "k", "k" }, + { "l", "l" }, + { ";", ";" }, + { "'", "'" }, + { "\\", "\\" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "z", "z" }, + { "x", "x" }, + { "c", "c" }, + { "v", "v" }, + { "b", "b" }, + { "n", "n" }, + { "m", "m" }, + { ",", "," }, + { ".", "." }, + { "/", "/" }, + { "←", NULL, 2, GUI_KEYFUNC_LEFT }, + { "→", NULL, 2, GUI_KEYFUNC_RIGHT }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "⇧", &qwertyUpper, 3, GUI_KEYFUNC_SHIFT_KB }, + { "!@#", &symbols, 3, GUI_KEYFUNC_CHANGE_KB }, + { "Space", " ", 10 }, + { "OK", NULL, 4, GUI_KEYFUNC_ENTER }, + { "Cancel", NULL, 4, GUI_KEYFUNC_CANCEL }, + {} + } + }, + }, + .width = 24 +}; + +static const struct GUIKeyboard qwertyUpper = { + .rows = { + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "1", "1" }, + { "2", "2" }, + { "3", "3" }, + { "4", "4" }, + { "5", "5" }, + { "6", "6" }, + { "7", "7" }, + { "8", "8" }, + { "9", "9" }, + { "0", "0" }, + { "_", "_" }, + { "⌫", NULL, 2, GUI_KEYFUNC_BACKSPACE }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "Q", "Q" }, + { "W", "W" }, + { "E", "E" }, + { "R", "R" }, + { "T", "T" }, + { "Y", "Y" }, + { "U", "U" }, + { "I", "I" }, + { "O", "O" }, + { "P", "P" }, + { "{", "}" }, + { "{", "}" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "A", "A" }, + { "S", "S" }, + { "D", "D" }, + { "F", "F" }, + { "G", "G" }, + { "H", "H" }, + { "J", "J" }, + { "K", "K" }, + { "L", "L" }, + { ":", ":" }, + { "\"", "\"" }, + { "|", "|" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "Z", "Z" }, + { "X", "X" }, + { "C", "C" }, + { "V", "V" }, + { "B", "B" }, + { "N", "N" }, + { "M", "M" }, + { "<", "<" }, + { ">", ">" }, + { "?", "?" }, + { "←", NULL, 2, GUI_KEYFUNC_LEFT }, + { "→", NULL, 2, GUI_KEYFUNC_RIGHT }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "⇪", &qwertyUpper, 3, GUI_KEYFUNC_CHANGE_KB }, + { "!@#", &symbols, 3, GUI_KEYFUNC_CHANGE_KB }, + { "Space", " ", 10 }, + { "OK", NULL, 4, GUI_KEYFUNC_ENTER }, + { "Cancel", NULL, 4, GUI_KEYFUNC_CANCEL }, + {} + } + }, + }, + .width = 24 +}; + +static const struct GUIKeyboard symbols = { + .rows = { + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "1", "1" }, + { "2", "2" }, + { "3", "3" }, + { "4", "4" }, + { "5", "5" }, + { "6", "6" }, + { "7", "7" }, + { "8", "8" }, + { "9", "9" }, + { "0", "0" }, + { "-", "-" }, + { "⌫", NULL, 2, GUI_KEYFUNC_BACKSPACE }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { ".", "." }, + { ",", "," }, + { ":", ":" }, + { ";", ";" }, + { "?", "?" }, + { "!", "!" }, + { "'", "'" }, + { "\"", "\"" }, + { "*", "*" }, + { "`", "`" }, + { "~", "~" }, + { "_", "_" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "<", "<" }, + { ">", ">" }, + { "{", "{" }, + { "}", "}" }, + { "+", "+" }, + { "=", "=" }, + { "#", "#" }, + { "&", "&" }, + { "$", "$" }, + {} + } + }, + { + .offset = 0, + .keys = (struct GUIKey[]) { + { "(", "(" }, + { ")", ")" }, + { "[", "[" }, + { "]", "]" }, + { "/", "/" }, + { "|", "|" }, + { "\\", "\\" }, + { "%", "%" }, + { "@", "@" }, + { "^", "^" }, + { "←", NULL, 2, GUI_KEYFUNC_LEFT }, + { "→", NULL, 2, GUI_KEYFUNC_RIGHT }, + {} + } + }, + { + .offset = 3, + .keys = (struct GUIKey[]) { + { "abc", &qwertyLower, 3, GUI_KEYFUNC_CHANGE_KB }, + { "Space", " ", 10 }, + { "OK", NULL, 4, GUI_KEYFUNC_ENTER }, + { "Cancel", NULL, 4, GUI_KEYFUNC_CANCEL }, + {} + } + }, + }, + .width = 24 +}; + +static void _backspace(char* string) { + size_t len = strlen(string); + if (len) { + string[len - 1] = '\0'; + } +} + +enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) { + GUIInvalidateKeys(params); + int curX = 0; + int curY = 0; + const struct GUIKey* curKey = NULL; + const struct GUIKeyboard* currentKbd = &qwertyLower; + const struct GUIKeyboard* prevKbd = currentKbd; + bool tempKbd = false; + while (true) { + uint32_t newInput = 0; + GUIPollInput(params, &newInput, 0); + unsigned cx, cy; + enum GUICursorState cursor = GUIPollCursor(params, &cx, &cy); + + if (newInput & (1 << GUI_INPUT_UP)) { + --curY; + if (curY < 0) { + curY = 4; + } + curKey = NULL; + } + if (newInput & (1 << GUI_INPUT_DOWN)) { + ++curY; + if (curY > 4) { + curY = 0; + } + curKey = NULL; + } + if (newInput & (1 << GUI_INPUT_LEFT)) { + --curX; + if (curX < 0) { + curX = currentKbd->width / 2; + } + curKey = NULL; + } + if (newInput & (1 << GUI_INPUT_RIGHT)) { + if (curKey) { + curX += curKey->width ? (curKey->width + 1) / 2 : 1; + } else { + ++curX; + } + if (curX >= currentKbd->width / 2) { + curX = 0; + } + curKey = NULL; + } + if (newInput & (1 << GUI_INPUT_BACK)) { + _backspace(keyboard->result); + } + if (newInput & (1 << GUI_INPUT_CANCEL)) { + return GUI_KEYBOARD_CANCEL; + } + + params->drawStart(); + if (params->guiPrepare) { + params->guiPrepare(); + } + + GUIFontPrint(params->font, 8, GUIFontHeight(params->font), GUI_ALIGN_LEFT, 0xFFFFFFFF, keyboard->title); + + unsigned height = GUIFontHeight(params->font) * 2; + unsigned width = (GUIFontGlyphWidth(params->font, 'W') | 1) + 1; // Round up + + int offset = (params->width - (width + 32) / 2 * currentKbd->width) / 2; + int row; + int col; + for (row = 0; row < 5; ++row) { + int y = params->height / 2 + (height + 16) * (row - 1); + int x = currentKbd->rows[row].offset; + for (col = 0; currentKbd->rows[row].keys[col].name; ++col) { + const struct GUIKey* key = ¤tKbd->rows[row].keys[col]; + int w = key->width ? key->width : 2; + if (row == curY) { + if (curX >= x / 2 && curX < (x + w) / 2) { + curKey = key; + } else if (col == 0 && curX < x / 2) { + curKey = key; + } else if (!currentKbd->rows[row].keys[col + 1].name && curX >= x / 2) { + curKey = key; + } + } + if (key->name[0]) { + int xOff = offset + x * (width + 32) / 2; + if (curKey == key) { + curX = x / 2; + GUIFontDraw9Slice(params->font, xOff, y, (width + 4) * w, height + 12, 0xFFFFFFFF, GUI_9SLICE_FILLED); + } else { + uint32_t fill = 0xFF606060; + if (key->function != GUI_KEYFUNC_INPUT_DATA) { + fill = 0xFFD0D0D0; + } + GUIFontDraw9Slice(params->font, xOff - 2, y - 2, (width + 4) * w + 4, height + 16, fill, GUI_9SLICE_FILL_ONLY); + } + GUIFontPrint(params->font, offset + (x * 2 + w) * (width + 32) / 4, y + height * 3 / 4 + 1, GUI_ALIGN_HCENTER | GUI_ALIGN_VCENTER, 0xFFFFFFFF, key->name); + } + x += w; + } + } + + if (newInput & (1 << GUI_INPUT_SELECT) && curKey) { + switch (curKey->function) { + case GUI_KEYFUNC_INPUT_DATA: + strncat(keyboard->result, curKey->data, keyboard->maxLen + 1); + if (tempKbd) { + tempKbd = false; + currentKbd = prevKbd; + } + break; + case GUI_KEYFUNC_BACKSPACE: + _backspace(keyboard->result); + break; + case GUI_KEYFUNC_SHIFT_KB: + tempKbd = true; + prevKbd = currentKbd; + currentKbd = curKey->data; + break; + case GUI_KEYFUNC_CHANGE_KB: + if (currentKbd == curKey->data) { + // Switching to itself while temporary removes temporary status; + // then switching once more goes back to previous keyboard + if (!tempKbd) { + currentKbd = prevKbd; + } + } else { + currentKbd = curKey->data; + } + tempKbd = false; + break; + case GUI_KEYFUNC_ENTER: + return GUI_KEYBOARD_DONE; + case GUI_KEYFUNC_CANCEL: + return GUI_KEYBOARD_CANCEL; + } + } + + int inputSize = keyboard->maxLen; + if (inputSize * width > params->width) { + inputSize = params->width / width - 2; + } + GUIFontDraw9Slice(params->font, (params->width - width * inputSize) / 2 - 8, height * 3, width * inputSize + 16, height + 8, 0xFFFFFFFF, GUI_9SLICE_EMPTY); + GUIFontPrint(params->font, (params->width - width * inputSize) / 2, height * 4 - 8, GUI_ALIGN_LEFT, 0xFFFFFFFF, keyboard->result); + + GUIDrawBattery(params); + GUIDrawClock(params); + + if (params->guiFinish) { + params->guiFinish(); + } + params->drawEnd(); + } +} + void _setup(struct mGUIRunner* runner) { runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation); runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble); diff --git a/src/util/gui/font-metrics.c b/src/util/gui/font-metrics.c index bf30cb4f0..8abc2968d 100644 --- a/src/util/gui/font-metrics.c +++ b/src/util/gui/font-metrics.c @@ -183,4 +183,16 @@ const struct GUIIconMetric defaultIconMetrics[] = { [GUI_ICON_9SLICE_CAP_SWW] = { 226, 8, 6, 8 }, [GUI_ICON_9SLICE_CAP_SSE] = { 232, 24, 8, 7 }, [GUI_ICON_9SLICE_CAP_SEE] = { 248, 8, 6, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_NW] = { 194, 33, 8, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_N] = { 202, 33, 12, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_NE] = { 214, 33, 8, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_W] = { 194, 41, 8, 12 }, + [GUI_ICON_9SLICE_FILL_ONLY_C] = { 202, 41, 12, 12 }, + [GUI_ICON_9SLICE_FILL_ONLY_E] = { 214, 41, 8, 12 }, + [GUI_ICON_9SLICE_FILL_ONLY_SW] = { 194, 55, 8, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_S] = { 202, 55, 12, 8 }, + [GUI_ICON_9SLICE_FILL_ONLY_SE] = { 214, 55, 8, 8 }, + [GUI_ICON_BACKSPACE] = { 82, 18, 12, 12 }, + [GUI_ICON_KBD_SHIFT] = { 114, 18, 12, 12 }, + [GUI_ICON_CAPSLOCK] = { 130, 18, 12, 12 }, }; diff --git a/src/util/gui/font.c b/src/util/gui/font.c index fbebd7937..0ad1cde4a 100644 --- a/src/util/gui/font.c +++ b/src/util/gui/font.c @@ -55,10 +55,22 @@ void GUIFontPrint(struct GUIFont* font, int x, int y, enum GUIAlignment align, u c = GUI_ICON_LEFT + c - 0x2190; icon = true; break; + case 0x232B: + c = GUI_ICON_BACKSPACE; + icon = true; + break; case 0x23E9: c = GUI_ICON_STATUS_FAST_FORWARD; icon = true; break; + case 0x21E7: + c = GUI_ICON_KBD_SHIFT; + icon = true; + break; + case 0x21EA: + c = GUI_ICON_CAPSLOCK; + icon = true; + break; case 0x1F507: c = GUI_ICON_STATUS_MUTE; icon = true; @@ -102,6 +114,12 @@ void GUIFontDraw9Slice(struct GUIFont* font, int x, int y, int width, int height GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SW); GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SE); break; + case GUI_9SLICE_FILL_ONLY: + GUIFontDrawIcon(font, x , y , GUI_ALIGN_LEFT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILL_ONLY_NW); + GUIFontDrawIcon(font, x + width, y , GUI_ALIGN_RIGHT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILL_ONLY_NE); + GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILL_ONLY_SW); + GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILL_ONLY_SE); + break; } unsigned offX, offY; @@ -123,6 +141,13 @@ void GUIFontDraw9Slice(struct GUIFont* font, int x, int y, int width, int height GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_E); GUIFontDrawIconSize(font, x + offX, y + offY, width - offX - endX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_C); break; + case GUI_9SLICE_FILL_ONLY: + GUIFontDrawIconSize(font, x + offX, y, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILL_ONLY_N); + GUIFontDrawIconSize(font, x, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILL_ONLY_W); + GUIFontDrawIconSize(font, x + offX, y + height - endY, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILL_ONLY_S); + GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILL_ONLY_E); + GUIFontDrawIconSize(font, x + offX, y + offY, width - offX - endX, height - offY - endY, color, GUI_ICON_9SLICE_FILL_ONLY_C); + break; case GUI_9SLICE_EMPTY_CAPPED: GUIFontDrawIcon(font, x + offX, y, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NNW); GUIFontDrawIcon(font, x, y + offY, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NWW);