Skip to content

Commit 49e071f

Browse files
committed
Overlay combo keys configuration UI
1 parent c6ed6e0 commit 49e071f

File tree

2 files changed

+235
-0
lines changed

2 files changed

+235
-0
lines changed

include/ComboConfig.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright (C) 2020 diwo
3+
*
4+
* This file is part of Tesla Menu.
5+
*
6+
* Tesla Menu is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Tesla Menu is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Tesla Menu. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#pragma once
21+
22+
#include <tesla.hpp>
23+
24+
#include <chrono>
25+
26+
enum ComboConfigState { STATE_CAPTURE, STATE_CAPTURE_RELEASE, STATE_CONFIRM, STATE_DONE, STATE_EXIT };
27+
28+
class ComboConfigFrame : public tsl::element::Frame {
29+
public:
30+
ComboConfigFrame(ComboConfigState *state): m_state(state) {}
31+
~ComboConfigFrame() {}
32+
33+
bool onClick(s64 key) override;
34+
35+
private:
36+
ComboConfigState *m_state;
37+
std::chrono::steady_clock::time_point m_lastPlusTime;
38+
};
39+
40+
class ComboConfig : public tsl::Gui {
41+
public:
42+
ComboConfig(): m_state(STATE_CAPTURE), m_combo(0), m_comboConfirm(0) {}
43+
~ComboConfig() {}
44+
45+
tsl::element::Element* createUI() override;
46+
void handleInputs(s64 keysDown, s64 keysHeld, JoystickPosition joyStickPosLeft, JoystickPosition joyStickPosRight, u32 touchX, u32 touchY) override;
47+
48+
void preDraw(tsl::Screen *screen) override;
49+
50+
protected:
51+
void handleCaptureInput(u64 keysDown, u64 keysHeld);
52+
void handleCaptureReleaseInput(u64 keysDown, u64 keysHeld);
53+
void handleConfirmInput(u64 keysDown, u64 keysHeld);
54+
void handleDoneInput(u64 keysDown, u64 keysHeld);
55+
56+
private:
57+
void filterAllowedKeys(u64 &keysDown, u64 &keysHeld);
58+
std::string comboGlyphs(u64 combo);
59+
60+
private:
61+
static const s32 CAPTURE_HOLD_MILLI = 2500;
62+
ComboConfigState m_state;
63+
u64 m_combo;
64+
u64 m_comboConfirm;
65+
std::chrono::steady_clock::time_point m_lastComboTime;
66+
s32 m_remainHoldMilli;
67+
};

source/ComboConfig.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* Copyright (C) 2020 diwo
3+
*
4+
* This file is part of Tesla Menu.
5+
*
6+
* Tesla Menu is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Tesla Menu is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Tesla Menu. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include <sstream>
21+
#include <iomanip>
22+
23+
#include <ComboConfig.hpp>
24+
25+
tsl::element::Element* ComboConfig::createUI() {
26+
auto frame = new ComboConfigFrame(&m_state);
27+
28+
frame->addElement(new tsl::element::CustomDrawer(0, 0, FB_WIDTH, FB_HEIGHT, [this](u16 x, u16 y, tsl::Screen *screen) {
29+
u32 line_y = 50;
30+
screen->drawString("Customize Combo Keys", false, 20, line_y, 30, tsl::a(0xFFFF));
31+
32+
line_y = 110;
33+
screen->drawString("Input new combo:", true, 20, line_y, 24, tsl::a(0xFFFF));
34+
screen->drawString(comboGlyphs(m_combo).c_str(), true, 20, line_y + 50, 32, tsl::a(0xFFFF));
35+
if (m_state == STATE_CAPTURE && m_combo) {
36+
double freq = (double)(CAPTURE_HOLD_MILLI - m_remainHoldMilli) / 1000;
37+
double off = sin(2 * 3.14 * freq) * 10 * m_remainHoldMilli / CAPTURE_HOLD_MILLI;
38+
screen->drawString("Hold still...", true, 20, line_y + 100 - off, 24, tsl::a(0xFFFF));
39+
if (m_remainHoldMilli < CAPTURE_HOLD_MILLI - 500) {
40+
std::stringstream ss;
41+
ss << std::fixed << std::setprecision(2) << ((float)m_remainHoldMilli / 1000) << "s";
42+
screen->drawString(ss.str().c_str(), true, 150, line_y + 100, 24, tsl::a(0xFFFF));
43+
}
44+
} else if (m_state > STATE_CAPTURE) {
45+
screen->drawString("Got it!", true, 20, line_y + 100, 24, tsl::a(0xFFFF));
46+
}
47+
48+
line_y = 270;
49+
if (m_state >= STATE_CAPTURE_RELEASE) {
50+
screen->drawString("Input combo again to confirm:", true, 20, line_y, 24, tsl::a(0xFFFF));
51+
if (m_state == STATE_CAPTURE_RELEASE) {
52+
auto keysHeldTime = std::chrono::duration_cast<std::chrono::milliseconds>(
53+
std::chrono::steady_clock::now() - m_lastComboTime).count();
54+
if (keysHeldTime > 2000)
55+
screen->drawString("(Release the buttons first)", true, 20, line_y + 40, 20, tsl::a(0xFFFF));
56+
}
57+
else if (m_state >= STATE_CONFIRM)
58+
screen->drawString(comboGlyphs(m_comboConfirm).c_str(), true, 20, line_y + 50, 32, tsl::a(0xFFFF));
59+
}
60+
61+
line_y = 400;
62+
if (m_state >= STATE_DONE) {
63+
screen->drawString("We're done!", true, 20, line_y, 24, tsl::a(0xFFFF));
64+
screen->drawString("Press \uE0A0 to continue", true, 20, line_y + 40, 24, tsl::a(0xFFFF));
65+
}
66+
67+
line_y = FB_HEIGHT - 20;
68+
if (m_state < STATE_DONE)
69+
screen->drawString("Press \uE0B5 twice to exit", true, 170, line_y, 24, tsl::a(0xFFFF));
70+
}));
71+
72+
return frame;
73+
}
74+
75+
void ComboConfig::handleInputs(
76+
s64 keysDown, s64 keysHeld,
77+
JoystickPosition joyStickPosLeft, JoystickPosition joyStickPosRight,
78+
u32 touchX, u32 touchY)
79+
{
80+
switch (m_state) {
81+
case STATE_CAPTURE:
82+
return handleCaptureInput(keysDown, keysHeld);
83+
case STATE_CAPTURE_RELEASE:
84+
return handleCaptureReleaseInput(keysDown, keysHeld);
85+
case STATE_CONFIRM:
86+
return handleConfirmInput(keysDown, keysHeld);
87+
case STATE_DONE:
88+
return handleDoneInput(keysDown, keysHeld);
89+
default:
90+
return;
91+
}
92+
}
93+
94+
void ComboConfig::handleCaptureInput(u64 keysDown, u64 keysHeld) {
95+
filterAllowedKeys(keysDown, keysHeld);
96+
auto now = std::chrono::steady_clock::now();
97+
if (keysHeld != m_combo) {
98+
m_lastComboTime = now;
99+
m_remainHoldMilli = CAPTURE_HOLD_MILLI;
100+
m_combo = keysHeld;
101+
}
102+
else if (m_combo) {
103+
m_remainHoldMilli = CAPTURE_HOLD_MILLI -
104+
std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastComboTime).count();
105+
if (m_remainHoldMilli <= 0) {
106+
m_state = STATE_CAPTURE_RELEASE;
107+
m_lastComboTime = now;
108+
}
109+
}
110+
}
111+
112+
void ComboConfig::handleCaptureReleaseInput(u64 keysDown, u64 keysHeld) {
113+
filterAllowedKeys(keysDown, keysHeld);
114+
if (!keysHeld)
115+
m_state = STATE_CONFIRM;
116+
}
117+
118+
void ComboConfig::handleConfirmInput(u64 keysDown, u64 keysHeld) {
119+
filterAllowedKeys(keysDown, keysHeld);
120+
m_comboConfirm = keysHeld;
121+
// Confirmation must be exact match
122+
if ((keysHeld == m_combo) && (keysDown & m_combo)) {
123+
tsl::KeyCombo::update(m_combo);
124+
m_state = STATE_DONE;
125+
}
126+
}
127+
128+
void ComboConfig::handleDoneInput(u64 keysDown, u64 keysHeld) {
129+
if (keysDown & KEY_A)
130+
m_state = STATE_EXIT;
131+
}
132+
133+
void ComboConfig::preDraw(tsl::Screen *screen) {
134+
screen->fillScreen(tsl::a({ 0x0, 0x0, 0x0, 0xD }));
135+
if (m_state == STATE_EXIT)
136+
Gui::goBack();
137+
}
138+
139+
bool ComboConfigFrame::onClick(s64 key) {
140+
if (key & KEY_PLUS) {
141+
auto now = std::chrono::steady_clock::now();
142+
auto elapsedMilli = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastPlusTime).count();
143+
if (*m_state < STATE_DONE && elapsedMilli < 500)
144+
tsl::Gui::goBack();
145+
else
146+
m_lastPlusTime = now;
147+
}
148+
return true;
149+
}
150+
151+
void ComboConfig::filterAllowedKeys(u64 &keysDown, u64 &keysHeld) {
152+
u64 allowedKeys = 0;
153+
for (auto &i : tsl::KeyCombo::KEYS_INFO) {
154+
allowedKeys |= i.key;
155+
}
156+
keysDown &= allowedKeys;
157+
keysHeld &= allowedKeys;
158+
}
159+
160+
std::string ComboConfig::comboGlyphs(u64 combo) {
161+
std::string str;
162+
for (auto &i : tsl::KeyCombo::KEYS_INFO) {
163+
if (combo & i.key) {
164+
str.append(i.glyph).append(" ");
165+
}
166+
}
167+
return str;
168+
}

0 commit comments

Comments
 (0)