mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
374 lines
13 KiB
C
374 lines
13 KiB
C
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <3ds.h>
|
|
|
|
#include "error.h"
|
|
#include "ui.h"
|
|
#include "../core/screen.h"
|
|
|
|
#define MAX_INPUT_SIZE 1024
|
|
|
|
typedef struct {
|
|
char input[MAX_INPUT_SIZE];
|
|
size_t inputPos;
|
|
|
|
bool shift;
|
|
bool capsLock;
|
|
|
|
float scrollPos;
|
|
u32 lastTouchY;
|
|
|
|
bool keyboardTouched;
|
|
u64 nextRepeatTime;
|
|
|
|
void* data;
|
|
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
|
void (*finished)(void* data, char* input);
|
|
void (*canceled)(void* data);
|
|
} kbd_data;
|
|
|
|
typedef struct {
|
|
char normal;
|
|
char special;
|
|
|
|
void (*press)(ui_view* view, kbd_data* data);
|
|
|
|
u32 x;
|
|
u32 y;
|
|
u32 width;
|
|
u32 height;
|
|
} LayoutKey;
|
|
|
|
static void kbd_backspace(ui_view* view, kbd_data* data) {
|
|
if(data->inputPos > 0) {
|
|
data->input[--data->inputPos] = '\0';
|
|
}
|
|
}
|
|
|
|
static void kbd_capslock(ui_view* view, kbd_data* data) {
|
|
data->capsLock = !data->capsLock;
|
|
}
|
|
|
|
static void kbd_shift(ui_view* view, kbd_data* data) {
|
|
data->shift = !data->shift;
|
|
}
|
|
|
|
static void kbd_cancel(ui_view* view, kbd_data* data) {
|
|
ui_pop();
|
|
|
|
if(data->canceled != NULL) {
|
|
data->canceled(view->data);
|
|
}
|
|
|
|
free(data);
|
|
free(view);
|
|
}
|
|
|
|
static void kbd_finish(ui_view* view, kbd_data* data) {
|
|
ui_pop();
|
|
|
|
if(data->finished != NULL) {
|
|
data->finished(view->data, data->input);
|
|
}
|
|
|
|
free(data);
|
|
free(view);
|
|
}
|
|
|
|
#define NUM_KEYS 56
|
|
|
|
static LayoutKey layout[NUM_KEYS] = {
|
|
// Row 1
|
|
{'`', '~', NULL, 1, 1, 21, 21},
|
|
{'1', '!', NULL, 22, 1, 21, 21},
|
|
{'2', '@', NULL, 43, 1, 21, 21},
|
|
{'3', '#', NULL, 64, 1, 21, 21},
|
|
{'4', '$', NULL, 85, 1, 21, 21},
|
|
{'5', '%', NULL, 106, 1, 21, 21},
|
|
{'6', '^', NULL, 127, 1, 21, 21},
|
|
{'7', '&', NULL, 148, 1, 21, 21},
|
|
{'8', '*', NULL, 169, 1, 21, 21},
|
|
{'9', '(', NULL, 190, 1, 21, 21},
|
|
{'0', ')', NULL, 211, 1, 21, 21},
|
|
{'-', '_', NULL, 232, 1, 21, 21},
|
|
{'=', '+', NULL, 253, 1, 21, 21},
|
|
{'\0', '\0', kbd_backspace, 274, 1, 42, 21},
|
|
|
|
// Row 2
|
|
{'\t', '\t', NULL, 1, 22, 31, 21},
|
|
{'q', 'Q', NULL, 32, 22, 21, 21},
|
|
{'w', 'W', NULL, 53, 22, 21, 21},
|
|
{'e', 'E', NULL, 74, 22, 21, 21},
|
|
{'r', 'R', NULL, 95, 22, 21, 21},
|
|
{'t', 'T', NULL, 116, 22, 21, 21},
|
|
{'y', 'Y', NULL, 137, 22, 21, 21},
|
|
{'u', 'U', NULL, 158, 22, 21, 21},
|
|
{'i', 'I', NULL, 179, 22, 21, 21},
|
|
{'o', 'O', NULL, 200, 22, 21, 21},
|
|
{'p', 'P', NULL, 221, 22, 21, 21},
|
|
{'[', '{', NULL, 242, 22, 21, 21},
|
|
{']', '}', NULL, 263, 22, 21, 21},
|
|
{'\\', '|', NULL, 284, 22, 32, 21},
|
|
|
|
// Row 3
|
|
{'\0', '\0', kbd_capslock, 1, 43, 36, 21},
|
|
{'a', 'A', NULL, 37, 43, 21, 21},
|
|
{'s', 'S', NULL, 58, 43, 21, 21},
|
|
{'d', 'D', NULL, 79, 43, 21, 21},
|
|
{'f', 'F', NULL, 100, 43, 21, 21},
|
|
{'g', 'G', NULL, 121, 43, 21, 21},
|
|
{'h', 'H', NULL, 142, 43, 21, 21},
|
|
{'j', 'J', NULL, 163, 43, 21, 21},
|
|
{'k', 'K', NULL, 184, 43, 21, 21},
|
|
{'l', 'L', NULL, 205, 43, 21, 21},
|
|
{';', ':', NULL, 226, 43, 21, 21},
|
|
{'\'', '"', NULL, 247, 43, 21, 21},
|
|
{'\n', '\n', NULL, 268, 43, 48, 21},
|
|
|
|
// Row 4
|
|
{'\0', '\0', kbd_shift, 1, 64, 47, 21},
|
|
{'z', 'Z', NULL, 48, 64, 21, 21},
|
|
{'x', 'X', NULL, 69, 64, 21, 21},
|
|
{'c', 'C', NULL, 90, 64, 21, 21},
|
|
{'v', 'V', NULL, 111, 64, 21, 21},
|
|
{'b', 'B', NULL, 132, 64, 21, 21},
|
|
{'n', 'N', NULL, 153, 64, 21, 21},
|
|
{'m', 'M', NULL, 174, 64, 21, 21},
|
|
{',', '<', NULL, 195, 64, 21, 21},
|
|
{'.', '>', NULL, 216, 64, 21, 21},
|
|
{'/', '?', NULL, 237, 64, 21, 21},
|
|
{'\0', '\0', kbd_shift, 258, 64, 58, 21},
|
|
|
|
// Row 5
|
|
{'\0', '\0', kbd_cancel, 1, 85, 94, 21},
|
|
{' ', ' ', NULL, 95, 85, 127, 21},
|
|
{'\0', '\0', kbd_finish, 222, 85, 94, 21},
|
|
};
|
|
|
|
static void kbd_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) {
|
|
kbd_data* kbdData = (kbd_data*) data;
|
|
|
|
if(hidKeysDown() & KEY_A) {
|
|
kbd_finish(view, kbdData);
|
|
return;
|
|
}
|
|
|
|
if(hidKeysDown() & KEY_B) {
|
|
kbd_cancel(view, kbdData);
|
|
return;
|
|
}
|
|
|
|
u32 kbdTextWidth = 0;
|
|
u32 kbdTextHeight = 0;
|
|
screen_get_texture_size(&kbdTextWidth, &kbdTextHeight, TEXTURE_KBD_TEXT_BG);
|
|
|
|
float kbdTextX = bx1 + (bx2 - bx1 - kbdTextWidth) / 2;
|
|
|
|
float inputHeight = 0;
|
|
screen_get_string_size_wrap(NULL, &inputHeight, kbdData->input, 0.5f, 0.5f, kbdTextX + kbdTextWidth);
|
|
|
|
if(hidKeysHeld() & KEY_TOUCH) {
|
|
touchPosition pos;
|
|
hidTouchRead(&pos);
|
|
|
|
u32 kbdWidth = 0;
|
|
u32 kbdHeight = 0;
|
|
screen_get_texture_size(&kbdWidth, &kbdHeight, TEXTURE_KBD_LAYOUT);
|
|
|
|
float kbdX = bx1 + (bx2 - bx1 - kbdWidth) / 2;
|
|
float kbdY = by2 - kbdHeight - 2;
|
|
|
|
if(pos.px >= kbdX && pos.py >= kbdY && pos.px < kbdX + kbdWidth && pos.py < kbdY + kbdHeight) {
|
|
if((hidKeysDown() & KEY_TOUCH) || (kbdData->keyboardTouched && osGetTime() >= kbdData->nextRepeatTime)) {
|
|
kbdData->keyboardTouched = true;
|
|
kbdData->nextRepeatTime = osGetTime() + ((hidKeysDown() & KEY_TOUCH) ? 500 : 100);
|
|
|
|
for(u32 i = 0; i < NUM_KEYS; i++) {
|
|
LayoutKey key = layout[i];
|
|
if(pos.px >= kbdX + key.x && pos.py >= kbdY + key.y && pos.px < kbdX + key.x + key.width && pos.py < kbdY + key.y + key.height) {
|
|
if(key.press != NULL) {
|
|
key.press(view, kbdData);
|
|
} else if(kbdData->inputPos < MAX_INPUT_SIZE) {
|
|
kbdData->input[kbdData->inputPos++] = (kbdData->shift || kbdData->capsLock) ? key.special : key.normal;
|
|
|
|
if(key.normal != key.special) {
|
|
kbdData->shift = false;
|
|
}
|
|
|
|
screen_get_string_size_wrap(NULL, &inputHeight, kbdData->input, 0.5f, 0.5f, kbdTextX + kbdTextWidth);
|
|
|
|
if(inputHeight > kbdTextHeight && kbdData->scrollPos < inputHeight - kbdTextHeight) {
|
|
kbdData->scrollPos = inputHeight - kbdTextHeight;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
kbdData->keyboardTouched = false;
|
|
}
|
|
|
|
if(!(hidKeysDown() & KEY_TOUCH)) {
|
|
float kbdTextX = bx1 + (bx2 - bx1 - kbdTextWidth) / 2;
|
|
float kbdTextY = by1 + 2;
|
|
|
|
if(pos.px >= kbdTextX && pos.py >= kbdTextY && pos.px < kbdTextX + kbdTextWidth && pos.py < kbdTextY + kbdTextHeight) {
|
|
kbdData->scrollPos += -((int) pos.py - (int) kbdData->lastTouchY);
|
|
}
|
|
}
|
|
|
|
kbdData->lastTouchY = pos.py;
|
|
} else {
|
|
kbdData->keyboardTouched = false;
|
|
}
|
|
|
|
if(kbdData->scrollPos > inputHeight - kbdTextHeight) {
|
|
kbdData->scrollPos = inputHeight - kbdTextHeight;
|
|
}
|
|
|
|
if(kbdData->scrollPos < 0) {
|
|
kbdData->scrollPos = 0;
|
|
}
|
|
}
|
|
|
|
static void kbd_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
|
|
kbd_data* kbdData = (kbd_data*) data;
|
|
|
|
if(kbdData->drawTop != NULL) {
|
|
kbdData->drawTop(view, kbdData->data, x1, y1, x2, y2);
|
|
}
|
|
}
|
|
|
|
static void kbd_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
|
|
kbd_data* kbdData = (kbd_data*) data;
|
|
|
|
touchPosition pos;
|
|
hidTouchRead(&pos);
|
|
|
|
u32 kbdTextWidth = 0;
|
|
u32 kbdTextHeight = 0;
|
|
screen_get_texture_size(&kbdTextWidth, &kbdTextHeight, TEXTURE_KBD_TEXT_BG);
|
|
|
|
float kbdTextX = x1 + (x2 - x1 - kbdTextWidth) / 2;
|
|
float kbdTextY = y1 + 2;
|
|
screen_draw_texture(TEXTURE_KBD_TEXT_BG, kbdTextX, kbdTextY, kbdTextWidth, kbdTextHeight);
|
|
|
|
screen_set_scissor(true, (u32) (kbdTextX + 1), (u32) (kbdTextY + 1), kbdTextWidth - 1, kbdTextHeight - 1);
|
|
screen_draw_string_wrap(kbdData->input, kbdTextX + 1, kbdTextY + 1 - kbdData->scrollPos, 0.5f, 0.5f, COLOR_TEXT, false, kbdTextX + kbdTextWidth);
|
|
screen_set_scissor(false, 0, 0, 0, 0);
|
|
|
|
u32 kbdTextFgWidth = 0;
|
|
u32 kbdTextFgHeight = 0;
|
|
screen_get_texture_size(&kbdTextFgWidth, &kbdTextFgHeight, TEXTURE_KBD_TEXT_FG);
|
|
|
|
float kbdTextFgX = kbdTextX + (((int) kbdTextWidth - (int) kbdTextFgWidth) / 2);
|
|
float kbdTextFgY = kbdTextY + (((int) kbdTextHeight - (int) kbdTextFgHeight) / 2);
|
|
screen_draw_texture(TEXTURE_KBD_TEXT_FG, kbdTextFgX, kbdTextFgY, kbdTextFgWidth, kbdTextFgHeight);
|
|
|
|
float inputHeight = 0;
|
|
screen_get_string_size_wrap(NULL, &inputHeight, kbdData->input, 0.5f, 0.5f, kbdTextX + kbdTextWidth);
|
|
|
|
if(inputHeight > kbdTextHeight) {
|
|
u32 scrollBarWidth = 0;
|
|
screen_get_texture_size(&scrollBarWidth, NULL, TEXTURE_SCROLL_BAR);
|
|
|
|
float scrollBarHeight = (kbdTextHeight / inputHeight) * kbdTextHeight;
|
|
|
|
float scrollBarX = kbdTextX + kbdTextWidth - scrollBarWidth;
|
|
float scrollBarY = kbdTextY + (kbdData->scrollPos / inputHeight) * kbdTextHeight;
|
|
|
|
screen_draw_texture(TEXTURE_SCROLL_BAR, scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
|
|
}
|
|
|
|
u32 kbdWidth = 0;
|
|
u32 kbdHeight = 0;
|
|
screen_get_texture_size(&kbdWidth, &kbdHeight, TEXTURE_KBD_LAYOUT);
|
|
|
|
float kbdX = x1 + (x2 - x1 - kbdWidth) / 2;
|
|
float kbdY = y2 - kbdHeight - 2;
|
|
screen_draw_texture(TEXTURE_KBD_LAYOUT, kbdX, kbdY, kbdWidth, kbdHeight);
|
|
|
|
for(u32 i = 0; i < NUM_KEYS; i++) {
|
|
LayoutKey key = layout[i];
|
|
|
|
char text[16];
|
|
if(key.press == kbd_backspace) {
|
|
snprintf(text, sizeof(text), "Del");
|
|
} else if(key.press == kbd_capslock) {
|
|
snprintf(text, sizeof(text), "Caps");
|
|
} else if(key.press == kbd_shift) {
|
|
snprintf(text, sizeof(text), "Shift");
|
|
} else if(key.press == kbd_cancel) {
|
|
snprintf(text, sizeof(text), "Cancel (B)");
|
|
} else if(key.press == kbd_finish) {
|
|
snprintf(text, sizeof(text), "Finish (A)");
|
|
} else if(key.normal == '\t') {
|
|
snprintf(text, sizeof(text), "Tab");
|
|
} else if(key.normal == '\n') {
|
|
snprintf(text, sizeof(text), "Enter");
|
|
} else if(key.normal == ' ') {
|
|
snprintf(text, sizeof(text), "Space");
|
|
} else {
|
|
snprintf(text, sizeof(text), "%c", (kbdData->shift || kbdData->capsLock) ? key.special : key.normal);
|
|
}
|
|
|
|
float textWidth = 0;
|
|
float textHeight = 0;
|
|
screen_get_string_size(&textWidth, &textHeight, text, 0.5f, 0.5f);
|
|
|
|
float textX = kbdX + key.x + (key.width - textWidth) / 2;
|
|
float textY = kbdY + key.y + (key.height - textHeight) / 2;
|
|
screen_draw_string(text, textX, textY, 0.5f, 0.5f, COLOR_TEXT, true);
|
|
|
|
if((key.press == kbd_capslock && kbdData->capsLock)
|
|
|| (key.press == kbd_shift && kbdData->shift)
|
|
|| ((hidKeysHeld() & KEY_TOUCH) && pos.px >= kbdX + key.x && pos.py >= kbdY + key.y && pos.px < kbdX + key.x + key.width && pos.py < kbdY + key.y + key.height)) {
|
|
screen_draw_texture(TEXTURE_KBD_PRESS_OVERLAY, kbdX + key.x, kbdY + key.y, key.width, key.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
void kbd_display(const char* name, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2),
|
|
void (*finished)(void* data, char* input),
|
|
void (*canceled)(void* data)) {
|
|
kbd_data* kbdData = (kbd_data*) calloc(1, sizeof(kbd_data));
|
|
if(kbdData == NULL) {
|
|
error_display(NULL, NULL, NULL, "Failed to allocate info data.");
|
|
return;
|
|
}
|
|
|
|
memset(kbdData->input, '\0', MAX_INPUT_SIZE);
|
|
kbdData->inputPos = 0;
|
|
|
|
kbdData->shift = false;
|
|
kbdData->capsLock = false;
|
|
|
|
kbdData->scrollPos = 0;
|
|
kbdData->lastTouchY = 0;
|
|
|
|
kbdData->keyboardTouched = false;
|
|
kbdData->nextRepeatTime = 0;
|
|
|
|
kbdData->data = data;
|
|
kbdData->drawTop = drawTop;
|
|
kbdData->finished = finished;
|
|
kbdData->canceled = canceled;
|
|
|
|
ui_view* view = (ui_view*) calloc(1, sizeof(ui_view));
|
|
if(view == NULL) {
|
|
error_display(NULL, NULL, NULL, "Failed to allocate UI view.");
|
|
return;
|
|
}
|
|
|
|
view->name = name;
|
|
view->info = "";
|
|
view->data = kbdData;
|
|
view->update = kbd_update;
|
|
view->drawTop = kbd_draw_top;
|
|
view->drawBottom = kbd_draw_bottom;
|
|
ui_push(view);
|
|
} |