diff --git a/lib/graphics/src/Surface.cpp b/lib/graphics/src/Surface.cpp index 2b33d7d7..4c15b761 100644 --- a/lib/graphics/src/Surface.cpp +++ b/lib/graphics/src/Surface.cpp @@ -146,7 +146,7 @@ namespace graphics &m_sprite, x, y, - m_transparent_color); + surface->m_transparent_color); } else { @@ -174,7 +174,7 @@ namespace graphics 0, scale, scale, - m_transparent_color); + surface->m_transparent_color); } else { @@ -310,7 +310,7 @@ namespace graphics int width = sprite.width(); int height = sprite.height(); - + // Open the file for writing std::ofstream outFile(filename.str(), std::ios::binary); if (!outFile.is_open()) { @@ -361,7 +361,7 @@ namespace graphics } } } - + // Add MCU to JPEG rc = jpg.addMCU(&jpe, ucMCU, 8); if (rc != 0) { @@ -374,7 +374,7 @@ namespace graphics // Finish encoding and get the compressed data int jpegSize = jpg.close(); - + // Write JPEG data to file outFile.write(reinterpret_cast(jpe.pOutput), jpegSize); outFile.close(); @@ -561,7 +561,45 @@ namespace graphics drawText(text, textPositionX, textPositionY, color); } - void Surface::blur(uint8_t radius) + Surface Surface::clone() const { + auto output = Surface(getWidth(), getHeight()); + + output.m_sprite.setBuffer(m_sprite.getBuffer(), m_sprite.width(), m_sprite.height()); + + return output; + } + + void * Surface::getBuffer() const { + return m_sprite.getBuffer(); + } + + void Surface::setBuffer(void *buffer, int32_t w, int32_t h) { + if (w == -1) { + w = getWidth(); + } + if (h == -1) { + h = getHeight(); + } + + m_sprite.setBuffer(buffer, w, h, m_sprite.getColorDepth()); + } + + void Surface::applyFilter(const Filter filter, const int32_t intensity) { + switch (filter) { + case BLUR: + blur(intensity); + break; + case LIGHTEN: + lighten(intensity); + break; + case DARKEN: + darken(intensity); + break; + default:; + } + } + + void Surface::blur(const int32_t radius) { // Copy auto copy = lgfx::LGFX_Sprite(); @@ -569,9 +607,9 @@ namespace graphics copy.createSprite(getWidth(), getHeight()); // Apply blur effect - for (uint16_t x = radius; x < getWidth() - radius; x++) + for (int32_t x = radius; x < m_sprite.width(); x++) { - for (uint16_t y = radius; y < getHeight() - radius; y++) + for (int32_t y = radius; y < m_sprite.height(); y++) { uint64_t sumR = 0; uint64_t sumG = 0; @@ -608,4 +646,27 @@ namespace graphics // Update the Surface copy.pushSprite(&m_sprite, 0, 0); } + + void Surface::fastBlur(int32_t radius) { + // TODO + } + + void Surface::lighten(const int32_t intensity) { + for (int32_t x = 0; x < getWidth(); x++) { + for (int32_t y = 0; y < getHeight(); y++) { + uint8_t r, g, b; + unpackRGB565(m_sprite.readPixel(x, y), &r, &g, &b); + + r = std::clamp(r + intensity, 0, 255); + g = std::clamp(g + intensity, 0, 255); + b = std::clamp(b + intensity, 0, 255); + + m_sprite.writePixel(x, y, packRGB565(r, g, b)); + } + } + } + + void Surface::darken(const int32_t intensity) { + lighten(-intensity); + } } // graphics \ No newline at end of file diff --git a/lib/graphics/src/Surface.hpp b/lib/graphics/src/Surface.hpp index efae2ddf..878e641e 100644 --- a/lib/graphics/src/Surface.hpp +++ b/lib/graphics/src/Surface.hpp @@ -81,7 +81,24 @@ namespace graphics // w and h are the width and height of the bounding box where the text will be centered void drawTextCentered(std::string &text, const int16_t x, const int16_t y, const uint16_t w = -1, const uint16_t h = -1, const bool horizontallyCentered = true, const bool verticallyCentered = true, const std::optional color = std::nullopt); - void blur(uint8_t radius); + [[nodiscard]] Surface clone() const; + + [[nodiscard]] void * getBuffer() const; + void setBuffer(void* buffer, int32_t w = -1, int32_t h = -1); + + enum Filter { + BLUR, + LIGHTEN, + DARKEN + }; + + void applyFilter(Filter filter, int32_t intensity); + + private: + void blur(int32_t radius); + void fastBlur(int32_t radius); + void lighten(int32_t intensity); + void darken(int32_t intensity); }; } // graphics diff --git a/lib/graphics/src/graphics.cpp b/lib/graphics/src/graphics.cpp index cc9702f2..b30515f8 100644 --- a/lib/graphics/src/graphics.cpp +++ b/lib/graphics/src/graphics.cpp @@ -125,7 +125,7 @@ graphics::GraphicsInitCode graphics::init() if (i2cDevicesCount == 0) { return ERROR_NO_TOUCHSCREEN; } - + if (i2cDevicesCount >= 2) { return ERROR_FAULTY_TOUCHSCREEN; } @@ -370,7 +370,7 @@ void graphics::setScreenOrientation(const EScreenOrientation screenOrientation) } } -LGFX* graphics::getLGFX() { +LGFX* graphics::getLCD() { return lcd.get(); } diff --git a/lib/graphics/src/graphics.hpp b/lib/graphics/src/graphics.hpp index 718755cf..ee557d23 100644 --- a/lib/graphics/src/graphics.hpp +++ b/lib/graphics/src/graphics.hpp @@ -66,7 +66,7 @@ namespace graphics EScreenOrientation getScreenOrientation(); void setScreenOrientation(EScreenOrientation screenOrientation); - LGFX* getLGFX(); + LGFX* getLCD(); #ifdef ESP_PLATFORM FT6236G* getTouchController(); #endif diff --git a/lib/gui/src/ElementBase.cpp b/lib/gui/src/ElementBase.cpp index 376d4d80..bdf95a9a 100644 --- a/lib/gui/src/ElementBase.cpp +++ b/lib/gui/src/ElementBase.cpp @@ -150,8 +150,8 @@ bool gui::ElementBase::updateAll() } update(); - - + + if(this->m_parent == nullptr) graphics::touchIsRead(); @@ -233,7 +233,7 @@ bool gui::ElementBase::update() bool isScrollingX = abs(m_lastTouchX - touchX) > SCROLL_STEP; bool isScrollingY = abs(m_lastTouchY - touchY) > SCROLL_STEP; bool isScrolling = isScrollingX || isScrollingY; - + if(isScrollingX) { gui::ElementBase* nearScrollableObject = getHigestXScrollableParent(); @@ -310,7 +310,7 @@ bool gui::ElementBase::update() lastEventTouchY = originTouchY; onReleased(); } - + globalPressedState = NOT_PRESSED; widgetPressed = nullptr; @@ -599,7 +599,7 @@ void gui::ElementBase::free() m_surface.reset(); setParentNotRendered(); - + for (auto child : m_children) { child->free(); @@ -621,4 +621,12 @@ bool gui::ElementBase::isInside() return false; return true; -} \ No newline at end of file +} + +std::shared_ptr gui::ElementBase::getSurface() { + return getAndSetSurface(); +} + +void gui::ElementBase::forceUpdate() { + localGraphicalUpdate(); +} diff --git a/lib/gui/src/ElementBase.hpp b/lib/gui/src/ElementBase.hpp index ee5472c0..82047fa3 100644 --- a/lib/gui/src/ElementBase.hpp +++ b/lib/gui/src/ElementBase.hpp @@ -93,6 +93,12 @@ namespace gui static int16_t lastEventTouchX, lastEventTouchY; uint16_t m_x, m_y; + + // WARNING : Don't ever expose this to the Lua API + std::shared_ptr getSurface(); + + void forceUpdate(); + protected: // variables générales uint16_t m_width, m_height; diff --git a/lib/gui/src/elements/Filter.cpp b/lib/gui/src/elements/Filter.cpp new file mode 100644 index 00000000..760a2ebd --- /dev/null +++ b/lib/gui/src/elements/Filter.cpp @@ -0,0 +1,44 @@ +#include "Filter.hpp" + +#include +#include + +#include "graphics.hpp" + +namespace gui::elements +{ + Filter::Filter(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height) + { + this->m_x = x; + this->m_y = y; + this->m_width = width; + this->m_height = height; + + m_screenSurface = std::make_shared(m_width, m_height); + } + + void Filter::render() { + std::cout << "RENDER FILTER" << std::endl; + + m_surface->pushSurface(m_screenSurface.get(), 0, 0); + } + + void Filter::apply() const { + std::cout << "APPLY FILTER" << std::endl; + + StandbyMode::triggerPower(); + + // Get screen + LGFX* lcd = graphics::getLCD(); + + // Copy screen zone to buffer + lcd->readRect(m_x, m_y, m_width, m_height, static_cast(m_screenSurface->getBuffer())); + + // Apply filter + m_screenSurface->applyFilter(graphics::Surface::LIGHTEN, 100); + m_screenSurface->applyFilter(graphics::Surface::DARKEN, 200); + m_screenSurface->applyFilter(graphics::Surface::BLUR, 3); + } + + Filter::~Filter() = default; +} diff --git a/lib/gui/src/elements/Filter.hpp b/lib/gui/src/elements/Filter.hpp new file mode 100644 index 00000000..bcf570a5 --- /dev/null +++ b/lib/gui/src/elements/Filter.hpp @@ -0,0 +1,29 @@ +#ifndef FILTER_HPP +#define FILTER_HPP + +#include "../ElementBase.hpp" +#include + +namespace gui::elements +{ + /** + * @deprecated Not compatible with device. + */ + class Filter final : public ElementBase + { + public: + typedef std::pair point_t; + + Filter(uint16_t x, uint16_t y, uint16_t width, uint16_t height); + ~Filter() override; + + void render() override; + + void apply() const; + + private: + std::shared_ptr m_screenSurface; + }; +} // gui::elements + +#endif //FILTER_HPP \ No newline at end of file diff --git a/lib/gui/src/elements/Keyboard.cpp b/lib/gui/src/elements/Keyboard.cpp index 17401ad5..746f8103 100644 --- a/lib/gui/src/elements/Keyboard.cpp +++ b/lib/gui/src/elements/Keyboard.cpp @@ -9,6 +9,7 @@ #include #include "Box.hpp" +#include "Filter.hpp" #include "Image.hpp" // Layouts @@ -39,8 +40,7 @@ constexpr uint8_t CAPS_ONCE = 1; constexpr uint8_t CAPS_LOCK = 2; namespace gui::elements { - Keyboard::Keyboard(const std::string& defaultText) - { + Keyboard::Keyboard(const std::string &defaultText) { m_buffer = defaultText; m_defaultText = defaultText; @@ -66,19 +66,19 @@ namespace gui::elements { m_keysCanvas = new Canvas(30, 140, 420, 160); addChild(m_keysCanvas); - m_layoutLowercase = new char*[4]; + m_layoutLowercase = new char *[4]; m_layoutLowercase[0] = new char[10]{'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'}; m_layoutLowercase[1] = new char[10]{'q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm'}; m_layoutLowercase[2] = new char[9]{KEY_CAPS, 'w', 'x', 'c', 'v', 'b', 'n', '\'', KEY_BACKSPACE}; m_layoutLowercase[3] = new char[3]{KEY_LAYOUT_NUMBERS, KEY_SPACE, KEY_EXIT}; - m_layoutUppercase = new char*[4]; + m_layoutUppercase = new char *[4]; m_layoutUppercase[0] = new char[10]{'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'}; m_layoutUppercase[1] = new char[10]{'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M'}; m_layoutUppercase[2] = new char[9]{KEY_CAPS, 'W', 'X', 'C', 'V', 'B', 'N', '\'', KEY_BACKSPACE}; m_layoutUppercase[3] = new char[3]{KEY_LAYOUT_NUMBERS, KEY_SPACE, KEY_EXIT}; - m_layoutNumbers = new char*[4]; + m_layoutNumbers = new char *[4]; m_layoutNumbers[0] = new char[10]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; m_layoutNumbers[1] = new char[10]{'+', '-', '*', '/', '(', ')', '[', ']', '<', '>'}; m_layoutNumbers[2] = new char[9]{KEY_CAPS, '_', ',', '.', ':', ';', '!', '?', KEY_BACKSPACE}; @@ -87,9 +87,12 @@ namespace gui::elements { m_currentLayout = LAYOUT_LOWERCASE; // Create label for text - m_label = new Label(30, 30, 380, 100); + m_label = new Label(30, 30, 380, 80); // m_label->setFont(graphics::ARIAL); m_label->setFontSize(24); + m_label->setCursorEnabled(true); + m_label->setText(m_buffer); + m_label->setCursorIndex(static_cast(m_buffer.length())); addChild(m_label); // Create images box (for better performances ?) @@ -138,63 +141,68 @@ namespace gui::elements { updateCapsIcon(); updateLayoutIcon(); + + m_trackpadActiveBox = new Box(192, 112, 96, 96); + m_trackpadActiveBox->setBackgroundColor(TFT_BLACK); + m_trackpadActiveBox->setRadius(8); + addChild(m_trackpadActiveBox); + + m_trackpadActiveIcon = new Image(storage::Path("system/keyboard/trackpad_active_icon.png"), 16, 16, 64, 64); + m_trackpadActiveIcon->load(TFT_BLACK); + m_trackpadActiveBox->addChild(m_trackpadActiveIcon); + + m_trackpadActiveBox->disable(); + + m_trackpadTicks = 0; + m_trackpadLastDeltaX = 0; } Keyboard::~Keyboard() = default; - void Keyboard::render() - { + void Keyboard::render() { m_surface->fillRect(0, 0, m_width, m_height, m_backgroundColor); // Input box drawInputBox(); - // Draw keys - drawKeys(); + if (!isTrackpadActive()) { + // Draw keys + drawKeys(); + } } - void Keyboard::widgetUpdate() - { + void Keyboard::widgetUpdate() { if (isTouched()) { // Get touch position int16_t touchX, touchY; getLastTouchPosRel(&touchX, &touchY); const char pressedKey = getKey(touchX, touchY); - if (pressedKey == KEY_NULL) - { + if (pressedKey == KEY_NULL) { return; } processKey(pressedKey); } - if (m_exitBox->isTouched()) - { + if (m_exitBox->isTouched()) { m_buffer = m_defaultText; // Reset text m_exit = true; } - if (m_confirmBox->isTouched()) - { + if (m_confirmBox->isTouched()) { m_exit = true; } - if (m_backspaceBox->isTouched()) - { - if (!m_buffer.empty()) - { - m_buffer.pop_back(); - } + if (m_backspaceBox->isTouched()) { + removeChar(); // Redraw input box drawInputBox(); } - if (m_capsBox->isTouched()) - { - switch (m_caps) - { + if (m_capsBox->isTouched()) { + switch (m_caps) { case CAPS_NONE: default: m_currentLayout = LAYOUT_UPPERCASE; @@ -213,20 +221,13 @@ namespace gui::elements { updateCapsIcon(); } - if (m_layoutBox->isTouched()) - { - if (m_currentLayout == LAYOUT_LOWERCASE || m_currentLayout == LAYOUT_UPPERCASE) - { + if (m_layoutBox->isTouched()) { + if (m_currentLayout == LAYOUT_LOWERCASE || m_currentLayout == LAYOUT_UPPERCASE) { m_currentLayout = LAYOUT_NUMBERS; - } - else if (m_currentLayout == LAYOUT_NUMBERS) - { - if (m_caps == CAPS_NONE) - { + } else if (m_currentLayout == LAYOUT_NUMBERS) { + if (m_caps == CAPS_NONE) { m_currentLayout = LAYOUT_LOWERCASE; - } - else - { + } else { m_currentLayout = LAYOUT_UPPERCASE; } } @@ -234,10 +235,11 @@ namespace gui::elements { drawKeys(); updateLayoutIcon(); } + + trackpadUpdate(); } - std::string Keyboard::getText() - { + std::string Keyboard::getText() { const std::string output = m_buffer; m_buffer = ""; @@ -245,10 +247,10 @@ namespace gui::elements { return output; } - void Keyboard::drawKeys() const - { + void Keyboard::drawKeys() const { // Reset default settings - m_keysCanvas->fillRect(0, 0, m_keysCanvas->getWidth(), m_keysCanvas->getHeight(), graphics::packRGB565(255, 255, 255)); + m_keysCanvas->fillRect(0, 0, m_keysCanvas->getWidth(), m_keysCanvas->getHeight(), + graphics::packRGB565(255, 255, 255)); // Draw every keys drawKeyRow(0, 10, getLayoutCharMap()[0]); @@ -257,12 +259,10 @@ namespace gui::elements { drawLastRow(); } - void Keyboard::drawKeyRow(const int16_t y, const uint8_t count, const char* keys) const - { + void Keyboard::drawKeyRow(const int16_t y, const uint8_t count, const char *keys) const { const float keyWidth = 420.0f / static_cast(count); - for (uint16_t i = 0; i < count; i++) - { + for (uint16_t i = 0; i < count; i++) { drawKey( static_cast(static_cast(i) * keyWidth), y, @@ -272,15 +272,13 @@ namespace gui::elements { } } - void Keyboard::drawKey(const int16_t x, const int16_t y, const uint16_t w, const char key) const - { + void Keyboard::drawKey(const int16_t x, const int16_t y, const uint16_t w, const char key) const { auto keyString = std::string(1, key); m_keysCanvas->drawTextCenteredInRect(x, y, w, 40, keyString, graphics::packRGB565(0, 0, 0), true, true, 32); } - void Keyboard::drawLastRow() const - { + void Keyboard::drawLastRow() const { // Draw spacebar m_keysCanvas->fillRect( 100, @@ -291,11 +289,9 @@ namespace gui::elements { ); } - char Keyboard::getKey(const int16_t x, const int16_t y) const - { + char Keyboard::getKey(const int16_t x, const int16_t y) const { // Check if the position is in the keyboard box - if (!(x >= 30 && x <= 450 && y >= 140 && y <= 300)) - { + if (!(x >= 30 && x <= 450 && y >= 140 && y <= 300)) { return KEY_NULL; } @@ -303,45 +299,34 @@ namespace gui::elements { uint8_t column; // Get the row - if (y <= 180) - { + if (y <= 180) { // First Row row = 0; // Get column column = getKeyCol(x, 10); - } - else if (y <= 220) - { + } else if (y <= 220) { // Second Row row = 1; // Get column column = getKeyCol(x, 10); - } - else if (y <= 260) - { + } else if (y <= 260) { // Third Row row = 2; // Get column column = getKeyCol(x, 9); - } - else - { + } else { // Last Row row = 3; // Get column - if (x <= 110) - { + if (x <= 110) { column = 0; - } - else if (x <= 370) - { + } else if (x <= 370) { column = 1; - } else - { + } else { column = 2; } } @@ -349,15 +334,12 @@ namespace gui::elements { return getLayoutCharMap()[row][column]; } - uint8_t Keyboard::getKeyCol(const int16_t x, const uint8_t keyCount) - { + uint8_t Keyboard::getKeyCol(const int16_t x, const uint8_t keyCount) { float boxX = 30; const float keyWidth = 420.0f / static_cast(keyCount); - for (uint8_t i = 0; i < keyCount; i++) - { - if (static_cast(x) >= boxX && static_cast(x) <= boxX + keyWidth) - { + for (uint8_t i = 0; i < keyCount; i++) { + if (static_cast(x) >= boxX && static_cast(x) <= boxX + keyWidth) { return i; } @@ -371,10 +353,8 @@ namespace gui::elements { * Execute the needed action for the key * @param key The key to process */ - void Keyboard::processKey(const char key) - { - switch (key) - { + void Keyboard::processKey(const char key) { + switch (key) { case KEY_NULL: case KEY_EXIT: case KEY_BACKSPACE: @@ -384,14 +364,14 @@ namespace gui::elements { // KEY_EXIT & KEY_BACKSPACE & KEY_CAPS & KEY_LAYOUT_STANDARD & KEY_LAYOUT_NUMBERS are handled directly in update function return; case KEY_SPACE: - m_buffer += " "; + addChar(' '); + break; default: - m_buffer += std::string(1, key); + addChar(key); - // Disable caps if not locked - if (m_caps == CAPS_ONCE) - { + // Disable caps if not locked + if (m_caps == CAPS_ONCE) { m_currentLayout = LAYOUT_LOWERCASE; m_caps = CAPS_NONE; @@ -405,15 +385,12 @@ namespace gui::elements { } // Redraw input box - drawInputBox();// <= Useless, because "markDirty" redraws it + drawInputBox(); // <= Useless, because "markDirty" redraws it } - void Keyboard::drawInputBox() const - { - if (m_buffer.empty()) - { - if (m_placeholder.empty()) - { + void Keyboard::drawInputBox() const { + if (m_buffer.empty()) { + if (m_placeholder.empty()) { m_label->setText(""); return; } @@ -421,19 +398,19 @@ namespace gui::elements { // Draw placeholder m_label->setTextColor(graphics::packRGB565(200, 200, 200)); m_label->setText(m_placeholder); - } - else - { + + m_label->setCursorEnabled(false); + } else { // Draw text m_label->setTextColor(graphics::packRGB565(0, 0, 0)); m_label->setText(m_buffer); + + m_label->setCursorEnabled(true); } } - void Keyboard::updateCapsIcon() const - { - switch (m_caps) - { + void Keyboard::updateCapsIcon() const { + switch (m_caps) { case CAPS_NONE: m_capsIcon0->enable(); m_capsIcon1->disable(); @@ -453,10 +430,8 @@ namespace gui::elements { } } - void Keyboard::updateLayoutIcon() const - { - switch (m_currentLayout) - { + void Keyboard::updateLayoutIcon() const { + switch (m_currentLayout) { case LAYOUT_LOWERCASE: case LAYOUT_UPPERCASE: m_layoutIcon0->enable(); @@ -470,20 +445,16 @@ namespace gui::elements { } } - bool Keyboard::hasExitKeyBeenPressed() const - { + bool Keyboard::hasExitKeyBeenPressed() const { return m_exit; } - void Keyboard::setPlaceholder(const std::string& placeholder) - { + void Keyboard::setPlaceholder(const std::string &placeholder) { m_placeholder = placeholder; } - char** Keyboard::getLayoutCharMap() const - { - switch (m_currentLayout) - { + char **Keyboard::getLayoutCharMap() const { + switch (m_currentLayout) { case LAYOUT_LOWERCASE: default: return m_layoutLowercase; @@ -493,4 +464,94 @@ namespace gui::elements { return m_layoutNumbers; } } -} // gui::elements \ No newline at end of file + + void Keyboard::trackpadUpdate() { + int16_t rawTouchX, rawTouchY; + graphics::getTouchPos(&rawTouchX, &rawTouchY); + + const bool wasTrackpadActive = isTrackpadActive(); + + // Check if finger is on screen + if ((rawTouchX != -1 && rawTouchY != -1) && isPointInTrackpad(m_lastTouchX, m_lastTouchY)) { + if (m_trackpadTicks < UINT8_MAX) { + m_trackpadTicks++; + } + + if (isTrackpadActive()) { + if (m_trackpadTicks == 10) { + // Do once, only when trackpad was just enabled + + m_trackpadActiveBox->enable(); + + m_trackpadLastDeltaX = 0; + + localGraphicalUpdate(); + } + + const int32_t deltaX = rawTouchX - m_lastTouchX; + std::string deltaXString = std::to_string(deltaX); + + constexpr int32_t stepsByChar = 8; + const int32_t toMove = (deltaX - m_trackpadLastDeltaX) / stepsByChar; + + if (toMove > 0) { + for (int i = 0; i < toMove; i++) { + m_label->setCursorIndex(static_cast(m_label->getCursorIndex() + 1)); + } + } else if (toMove < 0) { + for (int i = 0; i < -toMove; i++) { + m_label->setCursorIndex(static_cast(m_label->getCursorIndex() - 1)); + } + } + + if (abs(toMove) > 0) { + m_label->forceUpdate(); + // m_trackpadActiveBox->forceUpdate(); + } + + m_trackpadLastDeltaX += toMove * 10; + } + } else { + m_trackpadTicks = 0; + + if (wasTrackpadActive) { + // Do once + + // m_trackpadFilter->disable(); + m_trackpadActiveBox->disable(); + + localGraphicalUpdate(); + } + } + } + + bool Keyboard::isPointInTrackpad(const int16_t x, const int16_t y) const { + if (x < 110 || x > 370) return false; + if (y <= 260 || y > 300) return false; + + return true; + } + + bool Keyboard::isTrackpadActive() const { + return m_trackpadTicks >= 10; + } + + void Keyboard::addChar(const char value) { + m_buffer.insert(m_label->getCursorIndex(), 1, value); + + m_label->setCursorIndex(m_label->getCursorIndex() + 1); + } + + void Keyboard::removeChar() { + if (m_buffer.empty()) { + return; + } + if (m_label->getCursorIndex() <= 0) { + return; + } + + m_buffer.erase(m_label->getCursorIndex() - 1, 1); + + m_label->setCursorIndex(static_cast(m_label->getCursorIndex() - 1)); + } +} // gui::elements diff --git a/lib/gui/src/elements/Keyboard.hpp b/lib/gui/src/elements/Keyboard.hpp index b499eab8..6fac660a 100644 --- a/lib/gui/src/elements/Keyboard.hpp +++ b/lib/gui/src/elements/Keyboard.hpp @@ -8,18 +8,19 @@ #include "../ElementBase.hpp" #include "Box.hpp" #include "Canvas.hpp" +#include "Filter.hpp" #include "Image.hpp" #include "Label.hpp" -namespace gui::elements -{ - class Keyboard final : public ElementBase - { +namespace gui::elements { + class Keyboard final : public ElementBase { public: - explicit Keyboard(const std::string& defaultText = ""); + explicit Keyboard(const std::string &defaultText = ""); + ~Keyboard() override; void render() override; + void widgetUpdate() override; /** @@ -31,18 +32,18 @@ namespace gui::elements /** * @deprecated Please use "hasExitKeyBeenPressed()" */ - [[nodiscard]] bool quitting() const {return m_exit;} + [[nodiscard]] bool quitting() const { return m_exit; } [[nodiscard]] bool hasExitKeyBeenPressed() const; - void setPlaceholder(const std::string& placeholder); + void setPlaceholder(const std::string &placeholder); private: std::string m_buffer; std::string m_placeholder; std::string m_defaultText; - Label* m_label; + Label *m_label; bool m_exit = false; @@ -54,30 +55,37 @@ namespace gui::elements uint8_t m_caps; - Canvas* m_keysCanvas; + Canvas *m_keysCanvas; + + Image *m_capsIcon0; + Image *m_capsIcon1; + Image *m_capsIcon2; + Image *m_backspaceIcon; + Image *m_layoutIcon0; + Image *m_layoutIcon1; + Image *m_exitIcon; + Image *m_confirmIcon; + Image* m_trackpadActiveIcon; - Image* m_capsIcon0; - Image* m_capsIcon1; - Image* m_capsIcon2; - Image* m_backspaceIcon; - Image* m_layoutIcon0; - Image* m_layoutIcon1; - Image* m_exitIcon; - Image* m_confirmIcon; + Box *m_capsBox; + Box *m_layoutBox; + Box *m_backspaceBox; + Box *m_exitBox; + Box *m_confirmBox; + Box* m_trackpadActiveBox; - Box* m_capsBox; - Box* m_layoutBox; - Box* m_backspaceBox; - Box* m_exitBox; - Box* m_confirmBox; + uint8_t m_trackpadTicks; + int32_t m_trackpadLastDeltaX; void drawKeys() const; - void drawKeyRow(int16_t y, uint8_t count, const char* keys) const; + + void drawKeyRow(int16_t y, uint8_t count, const char *keys) const; + void drawKey(int16_t x, int16_t y, uint16_t w, char key) const; void drawLastRow() const; - char getKey(int16_t x, int16_t y) const; + [[nodiscard]] char getKey(int16_t x, int16_t y) const; static uint8_t getKeyCol(int16_t x, uint8_t keyCount); @@ -86,9 +94,20 @@ namespace gui::elements void drawInputBox() const; void updateCapsIcon() const; + void updateLayoutIcon() const; - char** getLayoutCharMap() const; + [[nodiscard]] char **getLayoutCharMap() const; + + void trackpadUpdate(); + + [[nodiscard]] bool isPointInTrackpad(int16_t x, int16_t y) const; + + [[nodiscard]] bool isTrackpadActive() const; + + void addChar(char value); + + void removeChar(); }; } // gui::elements diff --git a/lib/gui/src/elements/Label.cpp b/lib/gui/src/elements/Label.cpp index 329053ba..088ad36a 100644 --- a/lib/gui/src/elements/Label.cpp +++ b/lib/gui/src/elements/Label.cpp @@ -22,6 +22,9 @@ namespace gui::elements { m_y = y; m_width = width; m_height = height; + + m_hasCursor = false; + m_cursorIndex = 0; } Label::~Label() = default; @@ -35,7 +38,7 @@ namespace gui::elements { m_surface->setColor(this->m_backgroundColor); m_surface->setFontSize(this->m_fontSize); - std::vector lines = parse(); + auto [lines, m_cursorIndex, m_cursorLine] = parse(); for (size_t i = 0; i < lines.size(); i++) { @@ -92,48 +95,98 @@ namespace gui::elements { localGraphicalUpdate(); } - std::vector Label::parse(void) + Label::ParseDataOutput Label::parse(void) { - std::vector lines; - std::string currentLine = ""; + ParseDataOutput output; + output.m_cursorIndex = 0; + output.m_cursorLine = 0; + + std::string currentLine; + + uint16_t charIndex = 0; // Global text index - + uint16_t lineCharIndex = 0; // X position + uint16_t lineIndex = 0; // Y position for (char c : m_text) { + // Save cursor pos + if (m_hasCursor) + { + if (m_cursorIndex == charIndex) + { + // std::cout << "CURSOR POSITION MATCH ! " << charIndex << ", " << lineCharIndex << ", " << lineIndex << std::endl; + + // TODO : Better implementation + currentLine += '|'; + + output.m_cursorIndex = lineCharIndex; + output.m_cursorLine = lineIndex; + } + } + if (c == '\n') { - lines.push_back(currentLine); + output.m_lines.push_back(currentLine); currentLine = ""; + + lineIndex++; + lineCharIndex = 0; } else if (m_surface->getTextWidth(currentLine + c) <= getUsableWidth()) { currentLine += c; + + lineCharIndex++; } else if (c == ' ') { - lines.push_back(currentLine); + output.m_lines.push_back(currentLine); currentLine = ""; + + lineIndex++; + lineCharIndex = 0; } else { if (currentLine.empty()) { currentLine += c; + + lineCharIndex++; } else if (currentLine.back() == ' ') { currentLine += c; + + lineCharIndex++; } else { std::size_t lastSpace = currentLine.find_last_of(' '); if (lastSpace == std::string::npos) { - lines.push_back(currentLine); + output.m_lines.push_back(currentLine); currentLine = ""; currentLine += c; + + lineIndex++; + lineCharIndex = 1; } else { std::string firstPart = currentLine.substr(0, lastSpace); - lines.push_back(firstPart); + output.m_lines.push_back(firstPart); currentLine = currentLine.substr(lastSpace + 1); currentLine += c; + + lineIndex++; + lineCharIndex = lastSpace + 1; // TODO: Check if this is correct } } } + + charIndex++; + } + + if (m_hasCursor) + { + if (m_cursorIndex == m_text.length()) + { + // TODO : Better implementation + currentLine += '|'; + } } if (!currentLine.empty()) { - lines.push_back(currentLine); + output.m_lines.push_back(currentLine); } - return lines; + return output; } uint16_t Label::getUsableWidth(void) @@ -180,8 +233,8 @@ namespace gui::elements { } m_surface->setFontSize(this->m_fontSize); - - std::vector lines = parse(); + + const auto [lines, cursorIndex, cursorLine] = parse(); uint16_t out = getRadius() + getBorderSize()*2 + (m_surface->getTextHeight() + LINE_SPACING) * lines.size(); if(allocatedSprite) @@ -189,4 +242,31 @@ namespace gui::elements { return out; } + + bool Label::isCursorEnabled() const + { + return m_hasCursor; + } + + void Label::setCursorEnabled(const bool enable) + { + m_hasCursor = enable; + } + + uint16_t Label::getCursorIndex() const + { + return m_cursorIndex; + } + + void Label::setCursorIndex(const int16_t cursorIndex) + { + m_cursorIndex = cursorIndex; + + if (m_cursorIndex < 0) { + m_cursorIndex = 0; + } + if (m_cursorIndex > m_text.length()) { + m_cursorIndex = static_cast(m_text.length()); + } + } } // gui::elements \ No newline at end of file diff --git a/lib/gui/src/elements/Label.hpp b/lib/gui/src/elements/Label.hpp index 14a21d97..066f8deb 100644 --- a/lib/gui/src/elements/Label.hpp +++ b/lib/gui/src/elements/Label.hpp @@ -38,8 +38,21 @@ namespace gui::elements uint16_t getTextWidth(); uint16_t getTextHeight(); + [[nodiscard]] bool isCursorEnabled() const; + void setCursorEnabled(bool enable); + + [[nodiscard]] uint16_t getCursorIndex() const; + void setCursorIndex(int16_t cursorIndex); + private: - std::vector parse(void); + struct ParseDataOutput { + std::vector m_lines; + + uint16_t m_cursorIndex; // X + uint16_t m_cursorLine; // Y + }; + + ParseDataOutput parse(void); uint16_t getUsableWidth(void); // retourne la largeur réelle maximale utilisée par le texte (sans les bordure etc...) uint16_t getUsableHeight(void); @@ -48,6 +61,9 @@ namespace gui::elements std::string m_text; Alignement m_textVerticalAlignment; Alignement m_textHorizontalAlignment; + + bool m_hasCursor; + int16_t m_cursorIndex; }; } // gui::elements diff --git a/lib/gui/src/gui.hpp b/lib/gui/src/gui.hpp index 2475391b..a7c1c8a1 100644 --- a/lib/gui/src/gui.hpp +++ b/lib/gui/src/gui.hpp @@ -6,6 +6,7 @@ #include "elements/Box.hpp" #include "elements/Canvas.hpp" #include "elements/Window.hpp" +#include "elements/Filter.hpp" #include "elements/Label.hpp" #include "elements/Input.hpp" #include "elements/Image.hpp" diff --git a/lib/system/libsystem.cpp b/lib/system/libsystem.cpp index b63dce4d..047f746e 100644 --- a/lib/system/libsystem.cpp +++ b/lib/system/libsystem.cpp @@ -54,7 +54,7 @@ void libsystem::panic(const std::string &message, const bool restart) { const uint16_t screenWidth = graphics::getScreenWidth(); const uint16_t screenHeight = graphics::getScreenHeight(); - LGFX* lcd = graphics::getLGFX(); + LGFX* lcd = graphics::getLCD(); #ifdef ESP_PLATFORM FT6236G* touchController = graphics::getTouchController(); #endif @@ -183,7 +183,7 @@ bool libsystem::hasBootErrors() { } void libsystem::displayBootErrors() { - LGFX* lcd = graphics::getLGFX(); + LGFX* lcd = graphics::getLCD(); lcd->setFont(&DejaVu18); lcd->setTextColor(graphics::packRGB565(255, 100, 100)); diff --git a/storage/system/keyboard/trackpad_active_icon.png b/storage/system/keyboard/trackpad_active_icon.png new file mode 100644 index 00000000..db876fbd Binary files /dev/null and b/storage/system/keyboard/trackpad_active_icon.png differ