Implemented Keyboard; ToDo: Control buttons in vertical mode
This commit is contained in:
@ -30,6 +30,7 @@ set(headers
|
||||
src/graphdisplay.h
|
||||
src/icon.h
|
||||
src/iconinterface.h
|
||||
src/keyboardhelper.h
|
||||
src/icons/back.h
|
||||
src/icons/checked.h
|
||||
src/icons/unchecked.h
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include "changevaluedisplay_string.h"
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <espchrono.h>
|
||||
|
||||
// local includes
|
||||
#include "tftinstance.h"
|
||||
|
||||
@ -8,63 +11,62 @@ void espgui::ChangeValueDisplay<std::string>::start()
|
||||
Base::start();
|
||||
|
||||
m_value = this->getValue();
|
||||
|
||||
m_pressed = false;
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::initScreen()
|
||||
{
|
||||
Base::initScreen();
|
||||
|
||||
tft.drawRect(25, 75, 190, 65, TFT_WHITE);
|
||||
tft.drawRoundRect(10, 50, tft.width() - 20, 34, 5, TFT_WHITE);
|
||||
m_valueLabel.start();
|
||||
|
||||
tft.setTextFont(4);
|
||||
tft.setTextColor(TFT_WHITE);
|
||||
tft.drawString("Change value and", 10, 160);
|
||||
tft.drawString("press button to", 10, 185);
|
||||
tft.drawString("confirm and go", 10, 210);
|
||||
tft.drawString("back.", 10, 235);
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::update()
|
||||
{
|
||||
Base::update();
|
||||
|
||||
if (m_pressed)
|
||||
{
|
||||
m_pressed = false;
|
||||
if (auto result = this->setValue(m_value); result)
|
||||
confirm();
|
||||
else
|
||||
errorOccured(std::move(result).error());
|
||||
}
|
||||
m_keyboard.start();
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::redraw()
|
||||
{
|
||||
const auto now = espchrono::millis_clock::now().time_since_epoch().count() / 1000;
|
||||
Base::redraw();
|
||||
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft.setTextFont(4);
|
||||
m_valueLabel.redraw(m_value);
|
||||
|
||||
tft.drawRect(m_valueLabel.x() + tft.textWidth(m_value) + 3, m_valueLabel.y(), 2, tft.fontHeight(), (now % 1000 < 500) ? TFT_WHITE : TFT_BLACK);
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::setShownValue(std::string &&value)
|
||||
{
|
||||
tft.drawRect(m_valueLabel.x() + tft.textWidth(m_value) + 3, m_valueLabel.y(), 2, tft.fontHeight(), TFT_BLACK);
|
||||
m_value = std::move(value);
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::buttonPressed(Button button)
|
||||
{
|
||||
//Base::buttonPressed(button);
|
||||
|
||||
switch (button)
|
||||
{
|
||||
case Button::Left: this->back(); break;
|
||||
case Button::Right: m_pressed = true; break;
|
||||
default:;
|
||||
}
|
||||
m_keyboard.buttonPressed(button);
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::buttonReleased(Button button)
|
||||
{
|
||||
//Base::buttonReleased(button);
|
||||
|
||||
m_keyboard.buttonReleased(button);
|
||||
// TODO stop auto scroll
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::confirmValue()
|
||||
{
|
||||
if (auto result = this->setValue(m_value); result)
|
||||
confirm();
|
||||
else
|
||||
errorOccured(std::move(result).error());
|
||||
}
|
||||
|
||||
void espgui::ChangeValueDisplay<std::string>::removeLastCharFromShownValue()
|
||||
{
|
||||
if (auto val = this->shownValue(); !val.empty())
|
||||
{
|
||||
val.pop_back();
|
||||
this->setShownValue(std::move(val));
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,12 @@
|
||||
#include <string>
|
||||
|
||||
// local includes
|
||||
#include "changevaluedisplay.h"
|
||||
#include "displaywithtitle.h"
|
||||
#include "confirminterface.h"
|
||||
#include "backinterface.h"
|
||||
#include "changevaluedisplay.h"
|
||||
#include "confirminterface.h"
|
||||
#include "displaywithtitle.h"
|
||||
#include "errorhandlerinterface.h"
|
||||
#include "keyboardhelper.h"
|
||||
#include "widgets/label.h"
|
||||
|
||||
namespace espgui {
|
||||
@ -29,20 +30,24 @@ public:
|
||||
|
||||
void start() override;
|
||||
void initScreen() override;
|
||||
void update() override;
|
||||
void redraw() override;
|
||||
|
||||
void buttonPressed(Button button) override;
|
||||
void buttonReleased(Button button) override;
|
||||
|
||||
void confirmValue();
|
||||
|
||||
const std::string &shownValue() const { return m_value; }
|
||||
void setShownValue(std::string &&value) { m_value = std::move(value); }
|
||||
void setShownValue(std::string &&value);
|
||||
void appendToShownValue(char c) { m_value.push_back(c); }
|
||||
void appendToShownValue(const std::string &s) { m_value.append(s); }
|
||||
void removeLastCharFromShownValue();
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
bool m_pressed{};
|
||||
|
||||
Label m_valueLabel{26, 81}; // 188, 53
|
||||
Label m_valueLabel{12, 55}; // 188, 53
|
||||
Keyboard<ChangeValueDisplay<std::string>> m_keyboard{*this};
|
||||
};
|
||||
|
||||
} // namespace espgui
|
||||
|
285
src/keyboardhelper.h
Normal file
285
src/keyboardhelper.h
Normal file
@ -0,0 +1,285 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <futurecpp.h>
|
||||
|
||||
// esp-idf includes
|
||||
#include <esp_log.h>
|
||||
|
||||
// 3rdparty lib includes
|
||||
#include <fmt/core.h>
|
||||
#include <strutils.h>
|
||||
|
||||
// local includes
|
||||
#include "display.h"
|
||||
#include "screenmanager.h"
|
||||
#include "tftinstance.h"
|
||||
|
||||
namespace espgui {
|
||||
namespace {
|
||||
constexpr const char * const TAG = "KeyboardHelper";
|
||||
// all ascii chars from '!' to '~'
|
||||
constexpr const char * const KEYBOARD_SCREEN_1 = "\"!$%&/()=?QWERTZUIOPASDFGHJKL YXCVBNM";
|
||||
constexpr const char * const KEYBOARD_SCREEN_2 = "1234567890qwertzuiopasdfghjkl yxcvbnm";
|
||||
constexpr const char * const KEYBOARD_SCREEN_3 = "#'*+-,.;:_<|>\\@[]^_`{}~\"!$%&/ ()=?+-*";
|
||||
constexpr const char * const SHIFT = "Shift";
|
||||
constexpr const char * const SPACE = "Space";
|
||||
constexpr const char * const BACKSPACE = "Back";
|
||||
constexpr const char * const ENTER = "Enter";
|
||||
// constexpr const char * const KEYBOARD_SCREEN_3 = "1234567890!\"§$%&/()= ?@€{[]}\\~#+-*_,;.:";
|
||||
} // namespace
|
||||
|
||||
template<typename TDisplay = espgui::Display>
|
||||
class Keyboard
|
||||
{
|
||||
public:
|
||||
explicit Keyboard(TDisplay &display) : m_display(display) {}
|
||||
|
||||
void start()
|
||||
{
|
||||
const auto isLandscape = espgui::isLandscape();
|
||||
m_keyboard_start_y = isLandscape ? 98 : 120;
|
||||
|
||||
tft.fillRect(1, m_keyboard_start_y - 10, tft.width()-1, tft.height() - m_keyboard_start_y - (isLandscape ? 0 : 50), TFT_BLACK);
|
||||
tft.drawSunkenRect(1, m_keyboard_start_y - 10, tft.width()-1, tft.height() - m_keyboard_start_y - (isLandscape ? 0 : 50), TFT_WHITE, TFT_GREY, TFT_BLACK);
|
||||
|
||||
updateCharLength();
|
||||
drawKeyboard();
|
||||
}
|
||||
|
||||
void buttonPressed(Button button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Right:
|
||||
{
|
||||
if (m_char_index < m_char_length)
|
||||
m_display.setShownValue(m_display.shownValue() + m_keyset[m_char_index]);
|
||||
else if (m_char_index == m_char_length) // shift
|
||||
{
|
||||
nextScreen();
|
||||
}
|
||||
else if (m_char_index == m_char_length + 1) // space
|
||||
{
|
||||
m_display.setShownValue(m_display.shownValue() + " ");
|
||||
}
|
||||
else if (m_char_index == m_char_length + 2) // backspace
|
||||
{
|
||||
m_display.removeLastCharFromShownValue();
|
||||
}
|
||||
else if (m_char_index == m_char_length + 3) // enter
|
||||
{
|
||||
m_display.confirmValue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Left:
|
||||
popScreen();
|
||||
return;
|
||||
case Up:
|
||||
moveSelectorLeft();
|
||||
drawKeyboard(true);
|
||||
break;
|
||||
case Down:
|
||||
moveSelectorRight();
|
||||
drawKeyboard(true);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
void buttonReleased(Button button) {}
|
||||
|
||||
void moveSelectorUp()
|
||||
{
|
||||
m_last_char_index = m_char_index;
|
||||
|
||||
m_char_index -= 10;
|
||||
if (m_char_index < 0)
|
||||
m_char_index = (m_char_length + 4) - m_char_index;
|
||||
}
|
||||
|
||||
void moveSelectorDown()
|
||||
{
|
||||
m_last_char_index = m_char_index;
|
||||
|
||||
m_char_index += 10;
|
||||
if (m_char_index >= (m_char_length + 4))
|
||||
m_char_index = m_char_index - (m_char_length + 4);
|
||||
}
|
||||
|
||||
void moveSelectorLeft()
|
||||
{
|
||||
m_last_char_index = m_char_index;
|
||||
|
||||
if (m_char_index == 0)
|
||||
m_char_index = (m_char_length + 4) - 1;
|
||||
else
|
||||
m_char_index--;
|
||||
}
|
||||
|
||||
void moveSelectorRight()
|
||||
{
|
||||
m_last_char_index = m_char_index;
|
||||
|
||||
if (m_char_index == (m_char_length + 4) - 1)
|
||||
m_char_index = 0;
|
||||
else
|
||||
m_char_index++;
|
||||
}
|
||||
|
||||
private:
|
||||
void updateCharLength()
|
||||
{
|
||||
std::string tmpstr;
|
||||
|
||||
switch (m_current_screen)
|
||||
{
|
||||
case Screen::SCREEN_1:
|
||||
tmpstr = KEYBOARD_SCREEN_1;
|
||||
break;
|
||||
case Screen::SCREEN_2:
|
||||
tmpstr = KEYBOARD_SCREEN_2;
|
||||
break;
|
||||
case Screen::SCREEN_3:
|
||||
tmpstr = KEYBOARD_SCREEN_3;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unknown screen");
|
||||
return;
|
||||
}
|
||||
|
||||
m_keyboard = tmpstr;
|
||||
cpputils::stringReplaceAll(" ", "", tmpstr);
|
||||
m_char_length = tmpstr.length();
|
||||
m_keyset = tmpstr;
|
||||
}
|
||||
|
||||
void drawKeyboard(bool dont_draw_string = false)
|
||||
{
|
||||
size_t char_index{0};
|
||||
std::string keyboard_screen{m_keyboard};
|
||||
|
||||
std::vector<std::string> keyboard_lines;
|
||||
|
||||
for (size_t i = 0; i < keyboard_screen.size(); i += 10)
|
||||
{
|
||||
std::string line = keyboard_screen.substr(i, 10);
|
||||
if (cpputils::stringEndsWith(line, " "))
|
||||
line.pop_back();
|
||||
|
||||
keyboard_lines.push_back(line);
|
||||
}
|
||||
|
||||
const auto datum = tft.getTextDatum();
|
||||
tft.setTextDatum(MC_DATUM);
|
||||
|
||||
for (size_t i = 0; i < keyboard_lines.size(); i++)
|
||||
{ tft.setTextColor(TFT_WHITE);
|
||||
|
||||
tft.setTextColor(TFT_GREY);
|
||||
|
||||
const int32_t y = m_keyboard_start_y + (i * tft.fontHeight() + 9);
|
||||
std::string line = keyboard_lines[i];
|
||||
const int16_t x = tft.width() / (line.size() + 1);
|
||||
for (size_t j = 0; j < line.size(); j++)
|
||||
{
|
||||
const std::string _char{line[j]};
|
||||
const int32_t x_pos = x * (j + 1);
|
||||
const int32_t y_pos = y;
|
||||
|
||||
const auto width = tft.textWidth(_char) + 2;
|
||||
const auto height = tft.fontHeight() + 4;
|
||||
if (char_index == m_char_index)
|
||||
tft.drawRoundRect(x_pos-width/2-1, y_pos-height/2, width+2, height-4, 3, TFT_DARKGREY);
|
||||
|
||||
if (char_index == m_last_char_index)
|
||||
{
|
||||
tft.drawRoundRect(x_pos-width/2-1, y_pos-height/2, width+2, height-4, 3, TFT_BLACK);
|
||||
}
|
||||
|
||||
if (!dont_draw_string || char_index == m_char_index || char_index == m_last_char_index)
|
||||
{
|
||||
if (char_index == m_char_index || char_index == m_last_char_index)
|
||||
tft.setTextColor(char_index == m_last_char_index ? TFT_GREY : TFT_WHITE);
|
||||
|
||||
tft.drawString(_char, x_pos, y_pos);
|
||||
|
||||
if (char_index == m_char_index)
|
||||
tft.setTextColor(TFT_GREY);
|
||||
}
|
||||
char_index++;
|
||||
}
|
||||
}
|
||||
|
||||
tft.setTextDatum(datum);
|
||||
|
||||
// draw 3 extra buttons, back, space and enter (x=10, x=tft.width()/2, x=tft.width()-10)
|
||||
const int32_t y = m_keyboard_start_y + (keyboard_lines.size() * tft.fontHeight());
|
||||
|
||||
// align left (SHIFT, SPACE)
|
||||
tft.drawRoundRect(15-2, y-1, tft.textWidth(SHIFT)+4, tft.fontHeight()+2, 3, TFT_DARKGREY);
|
||||
tft.drawRoundRect(30+tft.textWidth(SHIFT)-2, y-1, tft.textWidth(SPACE)+4, tft.fontHeight()+2, 3, TFT_DARKGREY);
|
||||
|
||||
// align right (BACKSPACE, ENTER); align from tft.width()
|
||||
tft.drawRoundRect(tft.width()-30-tft.textWidth(ENTER)-tft.textWidth(BACKSPACE)-2, y-1, tft.textWidth(BACKSPACE)+4, tft.fontHeight()+2, 3, TFT_DARKGREY);
|
||||
tft.drawRoundRect(tft.width()-15-tft.textWidth(ENTER)-2, y-1, tft.textWidth(ENTER)+4, tft.fontHeight()+2, 3, TFT_DARKGREY);
|
||||
|
||||
// if (!dont_draw_string)
|
||||
{
|
||||
// align left (SHIFT, SPACE)
|
||||
if (m_char_index == m_char_length)
|
||||
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
else
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft.drawString(SHIFT, 15, y);
|
||||
|
||||
if (m_char_index == m_char_length + 1)
|
||||
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
else
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft.drawString(SPACE, 30+tft.textWidth(SHIFT), y);
|
||||
|
||||
// align right (BACKSPACE, ENTER); align from tft.width()
|
||||
if (m_char_index == m_char_length + 2)
|
||||
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
else
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft.drawString(BACKSPACE, tft.width()-30-tft.textWidth(ENTER)-tft.textWidth(BACKSPACE), y);
|
||||
|
||||
if (m_char_index == m_char_length + 3)
|
||||
tft.setTextColor(TFT_BLACK, TFT_WHITE);
|
||||
else
|
||||
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
||||
tft.drawString(ENTER, tft.width()-15-tft.textWidth(ENTER), y);
|
||||
}
|
||||
}
|
||||
|
||||
void nextScreen()
|
||||
{
|
||||
m_current_screen = static_cast<Screen>(static_cast<uint8_t>(m_current_screen) + uint8_t{1});
|
||||
if (m_current_screen >= Screen::SCREEN_MAX)
|
||||
m_current_screen = Screen::SCREEN_1;
|
||||
updateCharLength();
|
||||
start();
|
||||
}
|
||||
|
||||
int32_t m_char_length{0};
|
||||
std::string m_keyboard{KEYBOARD_SCREEN_1};
|
||||
std::string m_keyset{};
|
||||
|
||||
TDisplay &m_display;
|
||||
int32_t m_keyboard_start_y{0};
|
||||
|
||||
int32_t m_char_index{10};
|
||||
int32_t m_last_char_index{-1};
|
||||
|
||||
enum class Screen : uint8_t {
|
||||
SCREEN_1,
|
||||
SCREEN_2,
|
||||
SCREEN_3,
|
||||
SCREEN_MAX
|
||||
};
|
||||
Screen m_current_screen{Screen::SCREEN_2};
|
||||
};
|
||||
} // namespace espgui
|
Reference in New Issue
Block a user