From 26bf724f2b8d4a305b38b3a31b17115336e1810a Mon Sep 17 00:00:00 2001 From: Charles <37224242+Charlito33@users.noreply.github.com> Date: Sun, 26 May 2024 22:59:05 +0200 Subject: [PATCH] Refactor and enhance Keyboard widget Updated the Keyboard widget with various improvements including the addition of placeholder text, improved key detection, and changes to color and layout. Furthermore, old methods have been refactored to make them more efficient and maintainable. This commit also includes changes in lua_gui.cpp, lua_gui.hpp, Keyboard.cpp, and Keyboard.hpp files. --- lib/gui/src/elements/Keyboard.cpp | 390 ++++++++++++++-------- lib/gui/src/elements/Keyboard.hpp | 46 ++- lib/lua/src/lua_gui.cpp | 6 +- lib/lua/src/lua_gui.hpp | 2 +- storage/system/keyboard/confirm_icon.png | Bin 0 -> 877 bytes storage/system/keyboard/exit_icon.png | Bin 0 -> 1470 bytes storage/system/keyboard/layout_icon_0.png | Bin 0 -> 3149 bytes storage/system/keyboard/layout_icon_1.png | Bin 0 -> 1051 bytes 8 files changed, 281 insertions(+), 163 deletions(-) create mode 100644 storage/system/keyboard/confirm_icon.png create mode 100644 storage/system/keyboard/exit_icon.png create mode 100644 storage/system/keyboard/layout_icon_0.png create mode 100644 storage/system/keyboard/layout_icon_1.png diff --git a/lib/gui/src/elements/Keyboard.cpp b/lib/gui/src/elements/Keyboard.cpp index 8f9dbae5..6e1de994 100644 --- a/lib/gui/src/elements/Keyboard.cpp +++ b/lib/gui/src/elements/Keyboard.cpp @@ -11,6 +11,11 @@ #include "Box.hpp" #include "Image.hpp" +// Layouts +constexpr char LAYOUT_LOWERCASE = 0; +constexpr char LAYOUT_UPPERCASE = 1; +constexpr char LAYOUT_NUMBERS = 2; + // 0x0_ => Control chars constexpr char KEY_NULL = 0x00; constexpr char KEY_EXIT = 0x01; @@ -36,6 +41,8 @@ constexpr uint8_t CAPS_LOCK = 2; namespace gui::elements { Keyboard::Keyboard() { + //m_buffer = "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime mollitia, molestiae quas vel sint commodi repudiandae consequuntur voluptatum laborum numquam blanditiis harum quisquam eius sed odit fugiat iusto fuga praesentium optio, eaque rerum! Provident similique accusantium nemo autem. Veritatis obcaecati tenetur iure eius earum ut molestias architecto voluptate aliquam nihil, eveniet aliquid culpa officia aut!"; + if (graphics::getScreenOrientation() == graphics::LANDSCAPE) { m_width = graphics::getScreenWidth(); m_height = graphics::getScreenHeight(); @@ -49,7 +56,7 @@ namespace gui::elements { m_x = 0; m_y = 0; - m_backgroundColor = graphics::packRGB565(255, 0, 0); + m_backgroundColor = graphics::packRGB565(255, 255, 255); m_hasEvents = true; @@ -73,33 +80,60 @@ namespace gui::elements { m_layoutNumbers[2] = new char[9]{KEY_CAPS, '_', ',', '.', ':', ';', '!', '?', KEY_BACKSPACE}; m_layoutNumbers[3] = new char[3]{KEY_LAYOUT_STANDARD, KEY_SPACE, KEY_EXIT}; - m_currentLayout = m_layoutLowercase; + m_currentLayout = LAYOUT_LOWERCASE; + // Create label for text + m_label = new Label(30, 30, 380, 100); + // m_label->setFont(graphics::ARIAL); + m_label->setFontSize(24); + addChild(m_label); + + // Create images box (for better performances ?) m_capsBox = new Box(30, 220, 47, 40); - m_layoutBox = new Box(30, 260, 50, 40); + m_layoutBox = new Box(30, 260, 80, 40); m_backspaceBox = new Box(403, 220, 47, 40); - m_exitBox = new Box(400, 260, 50, 40); + m_exitBox = new Box(370, 260, 80, 40); + m_confirmBox = new Box(410, 30, 40, 100); + // Create images m_capsIcon0 = new Image(storage::Path("system/keyboard/caps_icon_0.png"), 0, 0, 40, 40); m_capsIcon1 = new Image(storage::Path("system/keyboard/caps_icon_1.png"), 0, 0, 40, 40); m_capsIcon2 = new Image(storage::Path("system/keyboard/caps_icon_2.png"), 0, 0, 40, 40); + m_backspaceIcon = new Image(storage::Path("system/keyboard/backspace_icon.png"), 0, 0, 40, 40); + m_layoutIcon0 = new Image(storage::Path("system/keyboard/layout_icon_0.png"), 20, 0, 40, 40); + m_layoutIcon1 = new Image(storage::Path("system/keyboard/layout_icon_1.png"), 20, 0, 40, 40); + m_exitIcon = new Image(storage::Path("system/keyboard/exit_icon.png"), 0, 0, 40, 40); + m_confirmIcon = new Image(storage::Path("system/keyboard/confirm_icon.png"), 0, 0, 40, 40); + // Load images into RAM m_capsIcon0->load(); m_capsIcon1->load(); m_capsIcon2->load(); + m_backspaceIcon->load(); + m_layoutIcon0->load(); + m_layoutIcon1->load(); + m_exitIcon->load(); + m_confirmIcon->load(); - m_capsBox->setBackgroundColor(TFT_GREEN); - m_layoutBox->setBackgroundColor(TFT_GREEN); - m_backspaceBox->setBackgroundColor(TFT_GREEN); - m_exitBox->setBackgroundColor(TFT_GREEN); - - m_capsIcon0->setBackgroundColor(graphics::packRGB565(0, 255, 255)); + // Add images to boxes m_capsBox->addChild(m_capsIcon0); - + m_capsBox->addChild(m_capsIcon1); + m_capsBox->addChild(m_capsIcon2); + m_backspaceBox->addChild(m_backspaceIcon); + m_layoutBox->addChild(m_layoutIcon0); + m_layoutBox->addChild(m_layoutIcon1); + m_exitBox->addChild(m_exitIcon); + m_confirmBox->addChild(m_confirmIcon); + + // Add boxes addChild(m_capsBox); addChild(m_layoutBox); addChild(m_backspaceBox); addChild(m_exitBox); + addChild(m_confirmBox); + + updateCapsIcon(); + updateLayoutIcon(); } Keyboard::~Keyboard() = default; @@ -118,30 +152,83 @@ namespace gui::elements { drawKeys(); } - void Keyboard::onReleased() + void Keyboard::widgetUpdate() { - // Get touch position - int16_t touchX, touchY; - getLastTouchPosRel(&touchX, &touchY); - - // std::cout << touchX << ", " << touchY << std::endl; - - // m_surface->drawRoundRect( - // static_cast(touchX - 5), - // static_cast(touchY - 5), - // 10, - // 10, - // 10, - // color - // ); - - const char pressedKey = getKey(touchX, touchY); - if (pressedKey == KEY_NULL) + if (isTouched()) { + // Get touch position + int16_t touchX, touchY; + getLastTouchPosRel(&touchX, &touchY); + + + const char pressedKey = getKey(touchX, touchY); + if (pressedKey == KEY_NULL) + { + return; + } + + processKey(pressedKey); + } + + if (m_exitBox->isTouched() || m_confirmBox->isTouched()) { - return; + // TODO : Implement callback ? + m_exit = true; + } + + if (m_backspaceBox->isTouched()) + { + if (!m_buffer.empty()) + { + m_buffer.pop_back(); + } + + // Redraw input box + drawInputBox(); + } + + if (m_capsBox->isTouched()) + { + switch (m_caps) + { + case CAPS_NONE: + default: + m_currentLayout = LAYOUT_UPPERCASE; + m_caps = CAPS_ONCE; + break; + case CAPS_ONCE: + m_currentLayout = LAYOUT_UPPERCASE; + m_caps = CAPS_LOCK; + break; + case CAPS_LOCK: + m_currentLayout = LAYOUT_LOWERCASE; + m_caps = CAPS_NONE; + break; + } + + updateCapsIcon(); } - processKey(pressedKey); + 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) + { + m_currentLayout = LAYOUT_LOWERCASE; + } + else + { + m_currentLayout = LAYOUT_UPPERCASE; + } + } + + markDirty(); + updateLayoutIcon(); + } } std::string Keyboard::getText() @@ -153,18 +240,20 @@ namespace gui::elements { return output; } - void Keyboard::drawKeys() + void Keyboard::drawKeys() const { + // Reset default settings + m_surface->setTextColor(graphics::packRGB565(0, 0, 0)); + m_surface->setFontSize(24); + // Draw every keys - drawKeyRow(140, 10, m_currentLayout[0]); - drawKeyRow(180, 10, m_currentLayout[1]); - drawKeyRow(220, 9, m_currentLayout[2]); + drawKeyRow(140, 10, getLayoutCharMap()[0]); + drawKeyRow(180, 10, getLayoutCharMap()[1]); + drawKeyRow(220, 9, getLayoutCharMap()[2]); drawLastRow(30, 260); - - m_surface->drawRect(0, 0, 1000, 10, TFT_GREEN); } - void Keyboard::drawKeyRow(const int16_t y, const uint8_t count, char* keys) + void Keyboard::drawKeyRow(const int16_t y, const uint8_t count, const char* keys) const { const float keyWidth = 420.0f / static_cast(count); @@ -179,51 +268,23 @@ namespace gui::elements { } } - void Keyboard::drawKey(const int16_t x, const int16_t y, const uint16_t w, char key) + void Keyboard::drawKey(const int16_t x, const int16_t y, const uint16_t w, const char key) const { - if (key == KEY_CAPS) - { - std::cout << "Caps : " << x << ", " << y << std::endl; - if (m_caps == CAPS_NONE) - { - //m_surface->drawImage(*m_capsIcon0, static_cast(x + 3), y); - } - else if (m_caps == CAPS_ONCE) - { - //m_surface->drawImage(*m_capsIcon1, static_cast(x + 3), y); - } - else - { - //m_surface->drawImage(*m_capsIcon2, static_cast(x + 3), y); - } - - return; - } - if (key == KEY_BACKSPACE) - { - std::cout << "Backspace : " << x << ", " << y << std::endl; - //m_surface->drawImage(*m_backspaceIcon, static_cast(x + 3), y); - - return; - } - auto keyString = std::string(1, key); // m_surface->drawRect(x, y, w, 40, graphics::packRGB565(0, 0, 0)); m_surface->drawTextCentered(keyString, x, y, w, 40); } - void Keyboard::drawLastRow(const int16_t x, const int16_t y) + void Keyboard::drawLastRow(const int16_t x, const int16_t y) const { - std::cout << "Layout : " << x << ", " << y << std::endl; - - m_surface->drawRect(x, y, 50, 40, graphics::packRGB565(0, 255, 0)); - m_surface->drawRect(static_cast(x + 50), y, 320, 40, graphics::packRGB565(0, 255, 0)); - m_surface->drawRect(static_cast(x + 370), y, 50, 40, graphics::packRGB565(0, 255, 0)); + // m_surface->drawRect(x, y, 80, 40, graphics::packRGB565(0, 255, 0)); + // m_surface->drawRect(static_cast(x + 80), y, 260, 40, graphics::packRGB565(0, 255, 0)); + /// m_surface->drawRect(static_cast(x + 340), y, 80, 40, graphics::packRGB565(0, 255, 0)); std::string thisStringIsUselessButNecessaryBecauseGraphicsLibIsBrokenAsOfNow; - if (m_currentLayout == m_layoutNumbers) + if (m_currentLayout == LAYOUT_NUMBERS) { thisStringIsUselessButNecessaryBecauseGraphicsLibIsBrokenAsOfNow = "ABC"; } @@ -255,7 +316,7 @@ namespace gui::elements { ); } - char Keyboard::getKey(const int16_t x, const int16_t y) + 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)) @@ -297,7 +358,7 @@ namespace gui::elements { row = 3; // Get column - if (x <= 80) + if (x <= 110) { column = 0; } @@ -310,7 +371,7 @@ namespace gui::elements { } } - return m_currentLayout[row][column]; + return getLayoutCharMap()[row][column]; } uint8_t Keyboard::getKeyCol(const int16_t x, const uint8_t keyCount) @@ -339,72 +400,28 @@ namespace gui::elements { { switch (key) { - case KEY_NULL: - return; - case KEY_EXIT: - std::cout << "KEYBOARD EXIT" << std::endl; - quit = true; - return; - case KEY_SPACE: - m_buffer += " "; - break; - case KEY_BACKSPACE: - if (!m_buffer.empty()) - { - m_buffer.pop_back(); - } - break; - case KEY_CAPS: - if (m_caps == CAPS_NONE) - { - m_currentLayout = m_layoutUppercase; - m_caps = CAPS_ONCE; - - markDirty(); - } - else if (m_caps == CAPS_ONCE) - { - m_currentLayout = m_layoutUppercase; - m_caps = CAPS_LOCK; - - markDirty(); - } else if (m_caps == CAPS_LOCK) - { - m_currentLayout = m_layoutLowercase; - m_caps = CAPS_NONE; - - markDirty(); - } - return; // QUIT - case KEY_LAYOUT_NUMBERS: - m_currentLayout = m_layoutNumbers; - markDirty(); - - return; - case KEY_LAYOUT_STANDARD: - if (m_caps == CAPS_NONE) - { - m_currentLayout = m_layoutLowercase; - markDirty(); - } - else - { - m_currentLayout = m_layoutUppercase; - markDirty(); - } - - return; - default: - m_buffer += std::string(1, key); - - // Disable caps if not locked - if (m_caps == CAPS_ONCE) - { - m_currentLayout = m_layoutLowercase; - m_caps = CAPS_NONE; - } - - break; + case KEY_NULL: + case KEY_EXIT: + case KEY_BACKSPACE: + case KEY_CAPS: + case KEY_LAYOUT_STANDARD: + case KEY_LAYOUT_NUMBERS: + // KEY_EXIT & KEY_BACKSPACE & KEY_CAPS & KEY_LAYOUT_STANDARD & KEY_LAYOUT_NUMBERS are handled directly in update function + return; + case KEY_SPACE: + m_buffer += " "; + break; + default: + m_buffer += std::string(1, key); + + // Disable caps if not locked + if (m_caps == CAPS_ONCE) + { + m_currentLayout = LAYOUT_LOWERCASE; + m_caps = CAPS_NONE; + } + + break; } // Redraw input box @@ -414,15 +431,30 @@ namespace gui::elements { markDirty(); } - void Keyboard::drawInputBox() + void Keyboard::drawInputBox() const { // m_surface->fillRect(30, 30, 420, 100, graphics::packRGB565(255, 255, 255)); - // Draw text - m_surface->setFont(graphics::ARIAL); - m_surface->setFontSize(24); - m_surface->setTextColor(graphics::packRGB565(0, 0, 0)); - m_surface->drawText(m_buffer, 30, 30); + m_label->setText(m_buffer); + + if (m_buffer.empty()) + { + if (m_placeholder.empty()) + { + m_label->setText(""); + return; + } + + // Draw placeholder + m_label->setTextColor(graphics::packRGB565(200, 200, 200)); + m_label->setText(m_placeholder); + } + else + { + // Draw text + m_label->setTextColor(graphics::packRGB565(0, 0, 0)); + m_label->setText(m_buffer); + } } void Keyboard::markDirty() @@ -430,4 +462,68 @@ namespace gui::elements { m_isDrawn = false; m_isRendered = false; } + + void Keyboard::updateCapsIcon() const + { + switch (m_caps) + { + case CAPS_NONE: + m_capsIcon0->enable(); + m_capsIcon1->disable(); + m_capsIcon2->disable(); + break; + case CAPS_ONCE: + m_capsIcon0->disable(); + m_capsIcon1->enable(); + m_capsIcon2->disable(); + break; + case CAPS_LOCK: + m_capsIcon0->disable(); + m_capsIcon1->disable(); + m_capsIcon2->enable(); + break; + default: ; + } + } + + void Keyboard::updateLayoutIcon() const + { + switch (m_currentLayout) + { + case LAYOUT_LOWERCASE: + case LAYOUT_UPPERCASE: + m_layoutIcon0->enable(); + m_layoutIcon1->disable(); + break; + case LAYOUT_NUMBERS: + m_layoutIcon0->disable(); + m_layoutIcon1->enable(); + break; + default: ; + } + } + + bool Keyboard::hasExitKeyBeenPressed() const + { + return m_exit; + } + + void Keyboard::setPlaceholder(const std::string& placeholder) + { + m_placeholder = placeholder; + } + + char** Keyboard::getLayoutCharMap() const + { + switch (m_currentLayout) + { + case LAYOUT_LOWERCASE: + default: + return m_layoutLowercase; + case LAYOUT_UPPERCASE: + return m_layoutUppercase; + case LAYOUT_NUMBERS: + return m_layoutNumbers; + } + } } // gui::elements \ No newline at end of file diff --git a/lib/gui/src/elements/Keyboard.hpp b/lib/gui/src/elements/Keyboard.hpp index 1d2ce6f6..c43971c6 100644 --- a/lib/gui/src/elements/Keyboard.hpp +++ b/lib/gui/src/elements/Keyboard.hpp @@ -8,6 +8,7 @@ #include "../ElementBase.hpp" #include "Box.hpp" #include "Image.hpp" +#include "Label.hpp" namespace gui::elements { @@ -18,7 +19,7 @@ namespace gui::elements ~Keyboard() override; void render() override; - void onReleased() override; + void widgetUpdate() override; /** * Returns the content of the keyboard's input AND CLEARS IT. @@ -26,13 +27,24 @@ namespace gui::elements */ std::string getText(); - bool quitting() {return quit;} + /** + * @deprecated Please use "hasExitKeyBeenPressed()" + */ + [[nodiscard]] bool quitting() const {return m_exit;} + + [[nodiscard]] bool hasExitKeyBeenPressed() const; + + void setPlaceholder(const std::string& placeholder); private: std::string m_buffer; - bool quit = false; + std::string m_placeholder; + + Label* m_label; - char **m_currentLayout; + bool m_exit = false; + + uint8_t m_currentLayout; char **m_layoutLowercase; char **m_layoutUppercase; @@ -43,28 +55,38 @@ namespace gui::elements 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; + + void drawKeys() 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 drawKeys(); - void drawKeyRow(int16_t y, uint8_t count, char* keys); - void drawKey(int16_t x, int16_t y, uint16_t w, char key); + void drawLastRow(int16_t x, int16_t y) const; - void drawLastRow(int16_t x, int16_t y); + char getKey(int16_t x, int16_t y) const; - char getKey(int16_t x, int16_t y); - uint8_t getKeyCol(int16_t x, uint8_t keyCount); + static uint8_t getKeyCol(int16_t x, uint8_t keyCount); void processKey(char key); - void drawInputBox(); + void drawInputBox() const; void markDirty(); + + void updateCapsIcon() const; + void updateLayoutIcon() const; + + char** getLayoutCharMap() const; }; } // gui::elements diff --git a/lib/lua/src/lua_gui.cpp b/lib/lua/src/lua_gui.cpp index 946968ad..42d001a5 100644 --- a/lib/lua/src/lua_gui.cpp +++ b/lib/lua/src/lua_gui.cpp @@ -154,14 +154,14 @@ void LuaGui::update() } } -std::string LuaGui::keyboard(std::string placeholder) +std::string LuaGui::keyboard(const std::string& placeholder) { gui::elements::Window win; graphics::setScreenOrientation(graphics::LANDSCAPE); - Keyboard key; - + Keyboard key; + key.setPlaceholder(placeholder); while (!hardware::getHomeButton() && !key.quitting()) { diff --git a/lib/lua/src/lua_gui.hpp b/lib/lua/src/lua_gui.hpp index 7a99c9bd..c3f63348 100644 --- a/lib/lua/src/lua_gui.hpp +++ b/lib/lua/src/lua_gui.hpp @@ -34,7 +34,7 @@ class LuaGui LuaRadio* radio(LuaWidget* parent, int x, int y); LuaWindow* window(); - std::string keyboard(std::string placeholder); + std::string keyboard(const std::string& placeholder); void del(LuaWidget* widget); diff --git a/storage/system/keyboard/confirm_icon.png b/storage/system/keyboard/confirm_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb4b804c9579c4131eb993188d5e9732cc259fa GIT binary patch literal 877 zcmV-z1CsoSP)}5 zg1&H&+;LBSdGEXAO-v+lnM*Mma9&*lYpp8@dx4e{vcNFX0pldI<%BmkI9Cb?sIc^QE~ zKs@~j;AZ8i`gQQJxWqj@J+h06qHtkhL3A-`mgNF>6hrY4E(>gr-9lM%>}kw7cSEJ+3e0SN>^kH^E4lM{iwYZMS9ACP1&m*ep8 za0zm5ZcYF*BrQe(LGm3*rqgNK?RE)NRh7|bR3KwU0zvW%Np5Ux&}=qKWJgB_x3{+i z^3z!vCQ@@?^#gDLuvPrIrCqld7Z))xF@bzO4*+mD99UUdK}$;u0DJ}D(L7T_?Pr~8 zYHF%vJq`>EFr7||!%xfYU<^S2$jAtGcXxGaAfPA;a=9FKc6LhkkByBX6bb>rX8@1N z=^$BDU&v;$Sj0)sTJE-UOjU*F^1e=?iW#_C`D>pYcMdv=8l~GUJdy?GW-)CoMr^GchG%ym8 zzYG%R<)VxZfld zBY_E$?Ca~3IKN-c9(j`XN;CBaz97l1tu5MYHhR5Y9v>eIpu3t(y@4RP&eQd_+PwdL z)zwlr0K5j!58xMyL%^$Q!|DOlwXWp%DTtNXV55RUS_od&ZV$yC zdI(M}g0~hH{0|1b^w2{fl!%x0U_lb61fnhJs=-Uxprt`Z!9sBMCb||(kqL+xo*ri2 z@txV3{o#aeE%Sw!oqeBY-uL@_-*?{knRm8{2sz#!+j~vmT}LipM&tr!L@r=P%g8Kamj>jJ$e(F1T1OHF5$g4_5;8qeqhV^Yt}Jf zAFvwK{i^v-1oorA2l(sTGOz?Z41D$hJbwH*t^Yk}XGh%9%| z_dvk+M2cO-b2ougH!-SyBBk&A`SWSo&I5aI+_=H?^faTRqxARpGcz;8l`B_x^5hAH zLV^DN{xRS?V02?+gKO8WarNp|=I7_RbLS4PUcF-f{{7p5Pk}?g=*r3p_wL=}=FOV` zc%H|=zyO<@o9y1bdspE1J+SxLvu7+WE^_hWMHUwq!!}1oMpWM=;5Yn@Y1gGomsnU> zV0?UB*;j$Dfp6;dI+kV8*Vm^w9qJ;+4}pblW}t{jI7=+cl5)8m{s*L3EQUi=tyaY_ zjEG$o(=?^gXh^)xq@qKgrSVcM7R7a4<$6~{#BrRYS^a~EEQv^jzvp>PeA>iVPtvJl z0h5Yh7$PF#x^AR?YHCVs+m4ViO*152sZ_)?%}6`ta#?KK4uh)IYE6NZ%jL)*8HS;Y z7!j~sF01OLBmo`A>1aSzj3Tn0SwJO%=Xp}CRznh&WraSv2(4@>!G^Sq?d3ejjZx(zlm`}Ac(**(vT%tW<3Y=wYHF^a#j z?Mc$9t32@V;lt|kW8hl=*L6etkt0XAfB$}@Jbn5!v|qSzp`(8E6z9~bQ|j_3{B-7* z!NEaJo;(@)JbLt~n=o1;A;!<~H=D$~t6d%#!QkMax_n5&yX)NC9CLGXsrMI)#csL- zVCT-A>hjOvcLn(J;K75TPm7$gYs6xV=b26DO2Bq741-dslxQ0o8sf~EGh5NkFu;o! zFSeqs%*Y~O9bkBPIIJj@O3cj6WaXsKIvzZDz~tm4z*qP=-4_5$OG}|op-^bm-21>7 zz^z-iGIMJ}H=BqC}l?wESMr%RMSML)oN0$R>NJeQmI7jDy4`g?D}h9TzJc} z67Tsh33xz)cS$`rODeIXcU?qM-&~X!da|8V^yFNE_vSPn>IwKa1zZ++lRIgT*>cS9 zv@+PXohXWBS^A@WSAv6wq?w`5QZ<9)K89h4ZQHtdvxN?Rx`XSw;y8{tjuRm&B31Dd(s1T_o)`MY*3Vh}kMfCeQGSX?cu%9z z5XW(v)=$@U^?t$=qz+>2T0kY@TF~fPn}#O>LhJDe#UIhLG5@q9Fx@w;&Gn#uB7w(Q zX+8$~L;T}3{YXmRT1(^b07*qoM6N<$g6)joHUIzs literal 0 HcmV?d00001 diff --git a/storage/system/keyboard/layout_icon_0.png b/storage/system/keyboard/layout_icon_0.png new file mode 100644 index 0000000000000000000000000000000000000000..034cc5fcd5d8217fbd1708623a37a1ecbf7b5df7 GIT binary patch literal 3149 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 literal 0 HcmV?d00001 diff --git a/storage/system/keyboard/layout_icon_1.png b/storage/system/keyboard/layout_icon_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a3aefaf545fd9709008e2f73bf6d0d4b51e61270 GIT binary patch literal 1051 zcmV+$1mydPP)N5#fEEquufwa!jMa`f)s%-#qO*} zyDQm2fw<5IgXNj`X1G#ayJc;64Kn*a@Yk4?q+(l@bggmh7i^wM>S;PIB<)FhT=0FI^%vKW zqz{tbxb_bUPu(NnH{i45<72L_u4ptGEG#Sld;|QH+4X>zqtS>sj!Q>VtJPRqvIoPj zz}LVP@C-NrJ_oRC04y&rv$nQY_9^UcTauzE5+Fc95ZK-I9lMW`WQ@t}ip}#p#c^zX zdzs%mNs8lGp68W(eBXC{LP;fwdy*6c!I)!`B-ZJLN6aP#0RfRmFG09vgUD=RAiwA*b)qY*$IcwV{{ z6}Y^-EcKn8-DI9dqhWv6NiQH6p0-`9& zTc^{(7(=_=W_Ne@uKRfd_@mLtZt5lQBT94Io${I^^VAqD#-YuH_Lh2{XU#4giBBcT z_x-W_iL#&%!|+yu?qU-NeyfUf6-AMXYdDwZwdLU60sEpn1ipkcUI2XGACoJJBEm4d zRpt!e13v?tpP%QQwOS1r50{;t9sBc#tmS>i82gt0S$bEi`7^?`@TQX-uHbsjJC1b z8)GH~@1`3cPLdrvjjDy5g!{MB%eZez77G%x{OFL|S60o0mVgtudm68D`cnFqx|igR zxK^5k?OgkIAW4#3V)w<7Qy|;wICKU>m8;-YWz~o?!KR#7r2?l)tK+AIxoq88<=axR?G>`iaEhqF()`H{sAE; V$YbC5BF6v#002ovPDHLkV1np)^yB~l literal 0 HcmV?d00001