/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 Bernardo Giordano
*
* 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 3 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, see .
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "hbkbd.hpp"
static const u32 buttonSpacing = 4;
static const u32 normalWidth = 92;
static const u32 bigWidth = 116;
static const u32 height = 60;
static const u32 margintb = 20;
static const u32 marginlr = 54;
static const u32 starty = 720 - 356 + margintb;
static const std::string letters = "1234567890@qwertyuiop+asdfghjkl_:zxcvbnm,.-/";
static std::vector buttons;
static size_t prevSelectedButtonIndex;
size_t hbkbd::count(void)
{
return buttons.size();
}
void hbkbd::hid(size_t& currentEntry)
{
static const size_t columns = 11;
u64 kHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
bool sleep = false;
if (kHeld & KEY_LEFT)
{
switch (currentEntry)
{
case 0: // 1
case columns: // q
case columns*2: // a
case columns*3: // z
case INDEX_CAPS:
break;
case INDEX_BACK: // back -> @
currentEntry = columns-1;
break;
case INDEX_RETURN: // return -> +
currentEntry = columns*2-1;
break;
case INDEX_OK: // OK -> space
currentEntry = INDEX_SPACE;
break;
case INDEX_SPACE: // space -> caps
currentEntry = INDEX_CAPS;
break;
default:
currentEntry--;
}
sleep = true;
}
else if (kHeld & KEY_RIGHT)
{
switch (currentEntry)
{
case 10: // @ -> back
currentEntry = INDEX_BACK;
break;
case 21: // + -> return
case 32: // : -> return
currentEntry = INDEX_RETURN;
break;
case 43: // /-> OK
case INDEX_SPACE: // space -> OK
currentEntry = INDEX_OK;
break;
case INDEX_CAPS: // caps -> space
currentEntry = INDEX_SPACE;
break;
case INDEX_BACK:
case INDEX_RETURN:
case INDEX_OK:
break;
default:
currentEntry++;
}
sleep = true;
}
else if (kHeld & KEY_UP)
{
switch (currentEntry)
{
case 0 ... 10: // 1 to @
case INDEX_BACK:
break;
case INDEX_CAPS: // caps -> x
currentEntry = 34;
break;
case INDEX_SPACE: // space -> .
currentEntry = 41;
break;
case INDEX_RETURN: // return -> back
currentEntry = INDEX_BACK;
break;
case INDEX_OK: // OK -> return
currentEntry = INDEX_RETURN;
break;
default:
currentEntry -= 11;
}
sleep = true;
}
else if (kHeld & KEY_DOWN)
{
switch (currentEntry)
{
case INDEX_CAPS:
case INDEX_SPACE:
case INDEX_OK:
break;
case 33 ... 35: // z x c -> caps
currentEntry = INDEX_CAPS;
break;
case 36 ... 43: // v b n m , . - / -> space
currentEntry = INDEX_SPACE;
break;
case INDEX_BACK: // back -> return
currentEntry = INDEX_RETURN;
break;
case INDEX_RETURN: // return -> OK
currentEntry = INDEX_OK;
break;
default:
currentEntry += 11;
}
sleep = true;
}
if (sleep)
{
svcSleepThread(FASTSCROLL_WAIT);
}
}
void hbkbd::init(void)
{
buttons.clear();
// fill with the above characters
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j < 11; j++)
{
HbkbdButton* button = new HbkbdButton(
marginlr + (buttonSpacing + normalWidth) * j,
starty + (buttonSpacing + height) * i,
normalWidth,
height,
COLOR_GREY_DARK,
COLOR_WHITE,
letters.substr(i*11+j, 1),
true
);
buttons.push_back(button);
}
}
HbkbdButton* backspace = new HbkbdButton(
marginlr + (buttonSpacing + normalWidth) * 11,
starty,
bigWidth,
height,
COLOR_WHITE,
COLOR_BLACK,
"back",
true
);
buttons.push_back(backspace);
HbkbdButton* returnb = new HbkbdButton(
marginlr + (buttonSpacing + normalWidth) * 11,
starty + height + 4,
bigWidth,
height * 2 + 4,
COLOR_GREY_MEDIUM,
COLOR_GREY_LIGHT,
"return",
true
);
buttons.push_back(returnb);
HbkbdButton* ok = new HbkbdButton(
marginlr + (buttonSpacing + normalWidth) * 11,
starty + height*3 + 4*3,
bigWidth,
height * 2 + 4,
COLOR_GREEN,
COLOR_BLACK,
"OK",
true
);
buttons.push_back(ok);
HbkbdButton* caps = new HbkbdButton(
marginlr + buttonSpacing + normalWidth,
starty + height*4 + 4*4,
normalWidth,
height,
COLOR_GREY_MEDIUM,
COLOR_WHITE,
"caps",
true
);
buttons.push_back(caps);
HbkbdButton* spacebar = new HbkbdButton(
marginlr + (buttonSpacing + normalWidth) * 3,
starty + height*4 + 4*4,
normalWidth*8 + buttonSpacing*7,
height,
COLOR_GREY_MEDIUM,
COLOR_WHITE,
"space",
true
);
buttons.push_back(spacebar);
// set first button as selected
buttons.at(0)->selected(true);
buttons.at(0)->invertColors();
prevSelectedButtonIndex = 0;
}
void hbkbd::exit(void)
{
for (size_t i = 0, sz = buttons.size(); i < sz; i++)
{
delete buttons[i];
}
}
static bool logic(std::string& str, size_t i)
{
if (buttons.at(i)->text().compare("caps") == 0)
{
std::locale loc;
bool islower = std::islower(buttons.at(11)->text()[0], loc);
for (size_t t = 0; t < letters.length(); t++)
{
std::string l = islower ? std::string(1, std::toupper(letters[t], loc)) : std::string(1, std::tolower(letters[t], loc));
buttons.at(t)->text(l);
}
}
else if (buttons.at(i)->text().compare("back") == 0)
{
if (!str.empty())
{
str.erase(str.length() - 1);
}
}
else if (buttons.at(i)->text().compare("space") == 0)
{
if (str.length() < CUSTOM_PATH_LEN)
{
str.append(" ");
}
}
else if (buttons.at(i)->text().compare("return") == 0)
{
//str.append("\n");
}
else if (buttons.at(i)->text().compare("OK") == 0)
{
return true;
}
else if (str.length() < CUSTOM_PATH_LEN)
{
str.append(buttons.at(i)->text());
}
return false;
}
std::string hbkbd::keyboard(const std::string& suggestion)
{
size_t index;
std::string str;
while (appletMainLoop() && !(hidKeysDown(CONTROLLER_P1_AUTO) & KEY_B))
{
hidScanInput();
index = prevSelectedButtonIndex;
// handle keys
hid(index);
if (index != prevSelectedButtonIndex)
{
buttons.at(prevSelectedButtonIndex)->selected(false);
buttons.at(prevSelectedButtonIndex)->invertColors();
prevSelectedButtonIndex = index;
buttons.at(index)->selected(true);
buttons.at(prevSelectedButtonIndex)->invertColors();
}
if (hidKeysDown(CONTROLLER_P1_AUTO) & KEY_A)
{
bool ret = logic(str, index);
if (ret)
{
return str.empty() ? suggestion : str;
}
}
framebuf = gfxGetFramebuffer(&framebuf_width, NULL);
memset(framebuf, 51, gfxGetFramebufferSize());
rectangle(marginlr, 140, 1280 - marginlr*2, 84, COLOR_GREY_MEDIUM);
rectangle(0, starty - margintb, 1280, 356, COLOR_GREY_DARKER);
u32 texth;
GetTextDimensions(font24, " ", NULL, &texth);
if (str.empty())
{
DrawText(font24, marginlr*2, 140 + (84 - texth) / 2, COLOR_GREY_LIGHT, suggestion.c_str());
}
else
{
DrawText(font24, marginlr*2, 140 + (84 - texth) / 2, COLOR_WHITE, str.c_str());
}
for (size_t i = 0, sz = buttons.size(); i < sz; i++)
{
if (buttons.at(i)->released())
{
bool ret = logic(str, i);
if (ret)
{
return str.empty() ? suggestion : str;
}
}
// selection logic
if (buttons.at(i)->held() && i != prevSelectedButtonIndex)
{
buttons.at(prevSelectedButtonIndex)->selected(false);
buttons.at(prevSelectedButtonIndex)->invertColors();
prevSelectedButtonIndex = i;
buttons.at(i)->selected(true);
buttons.at(i)->invertColors();
}
buttons.at(i)->draw();
}
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
return suggestion;
}