diff --git a/src/changevaluedisplay_string.cpp b/src/changevaluedisplay_string.cpp index 2fe3895..02f9ae0 100644 --- a/src/changevaluedisplay_string.cpp +++ b/src/changevaluedisplay_string.cpp @@ -26,6 +26,15 @@ void espgui::ChangeValueDisplay::initScreen(TftInterface &tft) m_keyboard.start(tft); m_needsClear = std::nullopt; + + m_locked = false; +} + +void espgui::ChangeValueDisplay::update() +{ + Base::update(); + + m_keyboard.update(); } void espgui::ChangeValueDisplay::redraw(TftInterface &tft) @@ -33,11 +42,11 @@ void espgui::ChangeValueDisplay::redraw(TftInterface &tft) Base::redraw(tft); const auto now_ts = espchrono::millis_clock::now().time_since_epoch(); - const auto char_width = tft.textWidth("A", 4); - const auto maxChars = (tft.width() - 20) / char_width; - const auto string = m_value.substr(std::max(0, static_cast(m_value.size()) - maxChars)); - - m_valueLabel.redraw(tft, string, TFT_WHITE, TFT_BLACK, 4); + const auto char_width = tft.textWidth(m_value, 4) / ((!m_value.empty()) ? m_value.size() : 1); + const auto maxChars = (tft.width() - 40) / char_width; + const auto substr_from = std::max(0U, m_value.size() < maxChars ? 0 : static_cast(m_value.size()) - maxChars); + ESP_LOGI("ChangeValueDisplay", "str: %s, strlen: %d, maxChars: %d, tft.width(): %d, char_width: %d, substr_from: %d, textWidth(\"a\")=%d, textWidth: %d", m_value.c_str(), m_value.size(), maxChars, tft.width(), char_width, substr_from, tft.textWidth("a", 4), tft.textWidth(m_value, 4)); + const auto string = m_value.substr(substr_from); if (m_needsClear) { @@ -45,14 +54,24 @@ void espgui::ChangeValueDisplay::redraw(TftInterface &tft) m_needsClear = std::nullopt; } - // tft.drawRect(m_valueLabel.x() + tft.textWidth(m_value, 4) + 3, m_valueLabel.y(), 2, tft.fontHeight(4), (now % 1000 < 500) ? TFT_WHITE : TFT_BLACK); + m_valueLabel.redraw(tft, string, TFT_WHITE, TFT_BLACK, 4); + tft.drawRect(m_valueLabel.x() + tft.textWidth(string, 4) + 3, m_valueLabel.y(), 2, tft.fontHeight(4), (now_ts % 1000ms < 500ms) ? TFT_WHITE : TFT_BLACK); m_keyboard.redraw(tft); + + m_locked = false; } void espgui::ChangeValueDisplay::setShownValue(std::string &&value) { + if (m_locked) + { + return; + } + + m_locked = true; + m_needsClear = std::move(m_value); m_value = std::move(value); } @@ -67,7 +86,6 @@ void espgui::ChangeValueDisplay::buttonReleased(Button button) { //Base::buttonReleased(button); m_keyboard.buttonReleased(button); - // TODO stop auto scroll } void espgui::ChangeValueDisplay::confirmValue() @@ -80,6 +98,11 @@ void espgui::ChangeValueDisplay::confirmValue() void espgui::ChangeValueDisplay::removeLastCharFromShownValue() { + if (m_locked) + { + return; + } + if (auto val = this->shownValue(); !val.empty()) { val.pop_back(); diff --git a/src/changevaluedisplay_string.h b/src/changevaluedisplay_string.h index c2cc7ba..5b08763 100644 --- a/src/changevaluedisplay_string.h +++ b/src/changevaluedisplay_string.h @@ -31,6 +31,7 @@ public: void start() override; void initScreen(TftInterface &tft) override; + void update() override; void redraw(TftInterface &tft) override; void buttonPressed(Button button) override; @@ -45,6 +46,8 @@ public: void removeLastCharFromShownValue(); private: + bool m_locked{false}; + std::string m_value; Label m_valueLabel{12, 55}; // 188, 53 diff --git a/src/keyboardhelper.h b/src/keyboardhelper.h index 5617fc2..02a4a1d 100644 --- a/src/keyboardhelper.h +++ b/src/keyboardhelper.h @@ -33,7 +33,7 @@ public: explicit Keyboard(TDisplay &display) : m_display(display) {} void start(TftInterface &tft); - + void update(); void redraw(TftInterface &tft); void buttonPressed(Button button); @@ -71,6 +71,18 @@ private: bool m_needsStart{}; bool m_needsRedraw{}; + + std::optional m_back_pressed_time{}; + std::optional m_confirm_pressed_time{}; + + struct ButtonHeldInfo + { + espchrono::millis_clock::time_point nextTimestamp; + int counter{}; + }; + + std::optional m_upHeld; + std::optional m_downHeld; }; template @@ -96,11 +108,15 @@ void Keyboard::moveSelectorDown() template void Keyboard::nextScreen() { + m_current_screen = static_cast(static_cast(m_current_screen) + uint8_t{1}); if (m_current_screen >= Screen::SCREEN_MAX) m_current_screen = Screen::SCREEN_1; + updateCharLength(); + m_needsStart = true; + m_needsRedraw = true; } template @@ -262,15 +278,22 @@ void Keyboard::start(TftInterface &tft) updateCharLength(); drawKeyboard(tft); + + m_upHeld = std::nullopt; + m_downHeld = std::nullopt; } template void Keyboard::redraw(TftInterface &tft) { + const auto isLandscape = espgui::isLandscape(tft); + if (m_needsStart) { m_needsStart = false; - drawKeyboard(tft, true); + tft.fillRect(1, m_keyboard_start_y - 10, tft.width()-1, tft.height() - m_keyboard_start_y - (isLandscape ? 0 : 30), TFT_BLACK); + tft.drawSunkenRect(1, m_keyboard_start_y - 10, tft.width()-1, tft.height() - m_keyboard_start_y - (isLandscape ? 0 : 30), TFT_WHITE, TFT_GREY, TFT_BLACK); + updateCharLength(); } if (m_needsRedraw) @@ -283,45 +306,122 @@ void Keyboard::redraw(TftInterface &tft) template void Keyboard::buttonPressed(Button button) { + using namespace std::chrono_literals; + 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(); - } + m_confirm_pressed_time = espchrono::millis_clock::now(); break; } case Left: - popScreen(); - return; + m_back_pressed_time = espchrono::millis_clock::now(); + break; case Up: moveSelectorLeft(); m_needsRedraw = true; + m_upHeld = ButtonHeldInfo { .nextTimestamp = espchrono::millis_clock::now() + 300ms }; break; case Down: moveSelectorRight(); m_needsRedraw = true; + m_downHeld = ButtonHeldInfo { .nextTimestamp = espchrono::millis_clock::now() + 300ms }; break; default:; } } template -void Keyboard::buttonReleased(espgui::Button button) {} +void Keyboard::buttonReleased(espgui::Button button) +{ + using namespace std::chrono_literals; + + switch (button) + { + case Left: + if (!m_back_pressed_time) + return; + + if (espchrono::ago(*m_back_pressed_time) < 350ms) + m_display.removeLastCharFromShownValue(); + + m_back_pressed_time = std::nullopt; + break; + case Right: + if (!m_confirm_pressed_time) + return; + + if (espchrono::ago(*m_confirm_pressed_time) < 350ms) + { + 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(); + } + } + m_confirm_pressed_time = std::nullopt; + break; + case Up: + m_upHeld = std::nullopt; + break; + case Down: + m_downHeld = std::nullopt; + break; + default:; + } +} + +template +void Keyboard::update() +{ + using namespace std::chrono_literals; + + const auto now = espchrono::millis_clock::now(); + if (m_back_pressed_time) + { + if (espchrono::ago(*m_back_pressed_time) > 350ms) + { + m_back_pressed_time = std::nullopt; + popScreen(); + } + } + + if (m_confirm_pressed_time) + { + if (espchrono::ago(*m_confirm_pressed_time) > 350ms) + { + m_confirm_pressed_time = std::nullopt; + nextScreen(); + } + } + + if (m_upHeld && now >= m_upHeld->nextTimestamp) + { + m_upHeld->nextTimestamp += m_upHeld->counter > 3 ? 100ms : 200ms; + m_upHeld->counter++; + moveSelectorLeft(); + m_needsRedraw = true; + } + if (m_downHeld && now >= m_downHeld->nextTimestamp) + { + m_downHeld->nextTimestamp += m_downHeld->counter > 3 ? 100ms : 200ms; + m_downHeld->counter++; + moveSelectorRight(); + m_needsRedraw = true; + } +} } // namespace espgui