Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Options menu and Combo Configuration Menu #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions include/keyconfig.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (C) 2020 diwo
*
* This file is part of Tesla Menu.
*
* Tesla Menu 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.
*
* Tesla Menu 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 Tesla Menu. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include <tesla.hpp>
#include <chrono>

class KeyConfig : public tsl::Gui {
public:
KeyConfig() : m_state(STATE_CAPTURE), m_combo(0), m_comboConfirm(0) {}

tsl::elm::Element* createUI() override;

bool handleInput(u64 keysDown, u64 keysHeld, touchPosition touchInput, JoystickPosition leftJoyStick, JoystickPosition rightJoyStick) override;

protected:
void handleExitMenuInput(u64 keysDown, u64 keysHeld);
void handleCaptureInput(u64 keysDown, u64 keysHeld);
void handleCaptureReleaseInput(u64 keysDown, u64 keysHeld);
void handleConfirmInput(u64 keysDown, u64 keysHeld);
void handleDoneInput(u64 keysDown, u64 keysHeld);

private:
enum ComboConfigState { STATE_CAPTURE, STATE_CAPTURE_RELEASE, STATE_CONFIRM, STATE_DONE };

void filterAllowedKeys(u64 &keysDown, u64 &keysHeld);
std::string comboGlyphs(u64 combo);

private:
static const std::chrono::milliseconds CAPTURE_HOLD_MILLI;

ComboConfigState m_state;
u64 m_combo;
u64 m_comboConfirm;
std::chrono::steady_clock::time_point m_lastPlusTime;
std::chrono::steady_clock::time_point m_lastComboTime;
std::chrono::milliseconds m_remainHoldMilli;
};
45 changes: 45 additions & 0 deletions include/options.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (C) 2020 diwo
*
* This file is part of Tesla Menu.
*
* Tesla Menu 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.
*
* Tesla Menu 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 Tesla Menu. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include <tesla.hpp>
#include <keyconfig.hpp>

class OptionsMenu : public tsl::Gui {
public:
tsl::elm::Element* createUI() override {
auto frame = new tsl::elm::OverlayFrame("Tesla Options", "");

auto list = new tsl::elm::List();

auto keysConfig = new tsl::elm::ListItem("Change combo keys");
keysConfig->setClickListener([](u64 keys) {
if (keys & KEY_A) {
tsl::changeTo<KeyConfig>();
return true;
}
return false;
});

list->addItem(keysConfig);
frame->setContent(list);

return frame;
}
};
173 changes: 173 additions & 0 deletions source/keyconfig.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* Copyright (C) 2020 diwo
*
* This file is part of Tesla Menu.
*
* Tesla Menu 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.
*
* Tesla Menu 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 Tesla Menu. If not, see <http://www.gnu.org/licenses/>.
*/

#include <keyconfig.hpp>

#include <sstream>
#include <iomanip>

using namespace std::chrono_literals;

const std::chrono::milliseconds KeyConfig::CAPTURE_HOLD_MILLI = 2500ms;

tsl::elm::Element* KeyConfig::createUI() {
auto COLOR_DEFAULT = a({ 0xE, 0xE, 0xE, 0xF });
auto COLOR_HEADING = a({ 0xC, 0xC, 0xC, 0xF });
auto COLOR_PENDING = a({ 0x8, 0x7, 0x1, 0xF });
auto COLOR_SUCCESS = a({ 0x0, 0xA, 0x2, 0xF });
auto COLOR_INFO = a({ 0x8, 0x8, 0x8, 0xF });

return new tsl::elm::CustomDrawer([=](tsl::gfx::Renderer *renderer, u16 x, u16 y, u16 w, u16 h) {
renderer->fillScreen(a({ 0x0, 0x0, 0x0, 0xD }));

u32 line_y = 50;
renderer->drawString("Change Combo Keys", false, 20, line_y, 30, a(0xFFFF));

line_y = 110;
renderer->drawString("Input new combo:", false, 20, line_y, 24, COLOR_HEADING);
auto comboColor = m_state == STATE_CAPTURE ? COLOR_PENDING : COLOR_SUCCESS;
renderer->drawString(comboGlyphs(m_combo).c_str(), false, 20, line_y + 50, 32, comboColor);
if (m_state == STATE_CAPTURE && m_combo) {
double freq = (CAPTURE_HOLD_MILLI - m_remainHoldMilli).count() / 1000;
double offset = sin(2 * 3.14 * freq) * 10 * m_remainHoldMilli / CAPTURE_HOLD_MILLI;
renderer->drawString("Hold still...", false, 20, line_y + 100 - offset, 24, COLOR_INFO);
if (m_remainHoldMilli < CAPTURE_HOLD_MILLI - 500ms) {
std::stringstream timerStream;
timerStream << std::fixed << std::setprecision(1) << ((float)m_remainHoldMilli.count() / 1000) << "s";
renderer->drawString(timerStream.str().c_str(), false, 160, line_y + 100, 30, COLOR_INFO);
}
} else if (m_state > STATE_CAPTURE) {
renderer->drawString("Got it!", false, 20, line_y + 100, 24, COLOR_DEFAULT);
}

line_y = 270;
if (m_state >= STATE_CAPTURE_RELEASE) {
renderer->drawString("Input combo again to confirm:", false, 20, line_y, 24, COLOR_HEADING);
if (m_state == STATE_CAPTURE_RELEASE) {
renderer->drawString("(Release the buttons first)", false, 20, line_y + 40, 20, COLOR_INFO);
}
else if (m_state >= STATE_CONFIRM) {
auto comboConfirmColor = m_state == STATE_CONFIRM ? COLOR_PENDING : COLOR_SUCCESS;
renderer->drawString(comboGlyphs(m_comboConfirm).c_str(), false, 20, line_y + 50, 32, comboConfirmColor);
}
}

line_y = 400;
if (m_state >= STATE_DONE) {
renderer->drawString("We're done!", false, 20, line_y, 24, COLOR_DEFAULT);
renderer->drawString("Press \uE0A0 to continue", false, 20, line_y + 60, 24, COLOR_DEFAULT);
}

line_y = tsl::cfg::FramebufferHeight - 20;
if (m_state < STATE_DONE)
renderer->drawString("Press \uE0B5 twice to exit", false, 170, line_y, 24, COLOR_INFO);
});
}

bool KeyConfig::handleInput(
u64 keysDown, u64 keysHeld, touchPosition touchInput,
JoystickPosition leftJoyStick, JoystickPosition rightJoyStick)
{
handleExitMenuInput(keysDown, keysHeld);

switch (m_state) {
case STATE_CAPTURE:
handleCaptureInput(keysDown, keysHeld);
return true;
case STATE_CAPTURE_RELEASE:
handleCaptureReleaseInput(keysDown, keysHeld);
return true;
case STATE_CONFIRM:
handleConfirmInput(keysDown, keysHeld);
return true;
case STATE_DONE:
handleDoneInput(keysDown, keysHeld);
return true;
default:
return false;
}
}

void KeyConfig::handleExitMenuInput(u64 keysDown, u64 keysHeld) {
if (keysDown & KEY_PLUS) {
auto now = std::chrono::steady_clock::now();
auto elapsedMilli = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastPlusTime);
if (m_state < STATE_DONE && elapsedMilli < 500ms)
tsl::goBack();
else
m_lastPlusTime = now;
}
}

void KeyConfig::handleCaptureInput(u64 keysDown, u64 keysHeld) {
filterAllowedKeys(keysDown, keysHeld);
auto now = std::chrono::steady_clock::now();
if (keysHeld != m_combo) {
m_lastComboTime = now;
m_remainHoldMilli = CAPTURE_HOLD_MILLI;
m_combo = keysHeld;
}
else if (m_combo) {
m_remainHoldMilli = CAPTURE_HOLD_MILLI -
std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastComboTime);
if (m_remainHoldMilli <= 0ms) {
m_state = STATE_CAPTURE_RELEASE;
}
}
}

void KeyConfig::handleCaptureReleaseInput(u64 keysDown, u64 keysHeld) {
filterAllowedKeys(keysDown, keysHeld);
if (!keysHeld)
m_state = STATE_CONFIRM;
}

void KeyConfig::handleConfirmInput(u64 keysDown, u64 keysHeld) {
filterAllowedKeys(keysDown, keysHeld);
m_comboConfirm = keysHeld;
// Confirmation must be exact match
if ((keysHeld == m_combo) && (keysDown & m_combo)) {
tsl::impl::updateCombo(m_combo);
m_state = STATE_DONE;
}
}

void KeyConfig::handleDoneInput(u64 keysDown, u64 keysHeld) {
if (keysDown & KEY_A)
tsl::goBack();
}

void KeyConfig::filterAllowedKeys(u64 &keysDown, u64 &keysHeld) {
u64 allowedKeys = 0;
for (auto &keyInfo : tsl::impl::KEYS_INFO) {
allowedKeys |= keyInfo.key;
}
keysDown &= allowedKeys;
keysHeld &= allowedKeys;
}

std::string KeyConfig::comboGlyphs(u64 combo) {
std::string str;
for (auto &keyInfo : tsl::impl::KEYS_INFO) {
if (combo & keyInfo.key) {
str.append(keyInfo.glyph).append(" ");
}
}
return str;
}
23 changes: 21 additions & 2 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <switch/nro.h>
#include <switch/nacp.h>

#include <options.hpp>

#include "logo_bin.h"

constexpr int Module_OverlayLoader = 348;
Expand Down Expand Up @@ -73,14 +75,21 @@ class TeslaMenuFrame : public tsl::elm::OverlayFrame {
~TeslaMenuFrame() {}

virtual void draw(tsl::gfx::Renderer *renderer) override {
OverlayFrame::draw(renderer);
renderer->fillScreen(a({ 0x0, 0x0, 0x0, 0xD }));

renderer->drawBitmap(20, 20, 84, 31, logo_bin);
renderer->drawString(envGetLoaderInfo(), false, 20, 68, 15, renderer->a(0xFFFF));

renderer->drawRect(15, 720 - 73, tsl::cfg::FramebufferWidth - 30, 1, a(0xFFFF));
renderer->drawString("\uE0EF Options \uE0E0 OK", false, 30, 693, 23, a(0xFFFF));

if (this->m_contentElement != nullptr)
this->m_contentElement->frame(renderer);
}
};

static TeslaMenuFrame *rootFrame = nullptr;
static tsl::Gui *guiMain = nullptr;

static void rebuildUI() {
auto *overlayList = new tsl::elm::List();
Expand Down Expand Up @@ -132,12 +141,22 @@ class GuiMain : public tsl::Gui {
~GuiMain() { }

tsl::elm::Element* createUI() override {
guiMain = this;

rootFrame = new TeslaMenuFrame();

rebuildUI();

return rootFrame;
}

bool handleInput(u64 keysDown, u64 keysHeld, touchPosition touchInput, JoystickPosition leftJoyStick, JoystickPosition rightJoyStick) {
if (keysDown & KEY_PLUS) {
tsl::changeTo<OptionsMenu>();
return true;
}
return false;
}
};

class OverlayTeslaMenu : public tsl::Overlay {
Expand All @@ -146,7 +165,7 @@ class OverlayTeslaMenu : public tsl::Overlay {
~OverlayTeslaMenu() { }

void onShow() override {
if (rootFrame != nullptr) {
if (rootFrame != nullptr && tsl::Overlay::get()->getCurrentGui().get() == guiMain) {
tsl::Overlay::get()->getCurrentGui()->removeFocus();
rebuildUI();
rootFrame->invalidate();
Expand Down