diff --git a/module-gui/gui/input/Translator.cpp b/module-gui/gui/input/Translator.cpp index 240989c07..5b8c0db6a 100644 --- a/module-gui/gui/input/Translator.cpp +++ b/module-gui/gui/input/Translator.cpp @@ -158,77 +158,6 @@ namespace gui return evt; } - /// profiles cache - load once for all - class Profiles - { - private: - const char *profilesFolder = "assets/profiles"; - std::map profiles = {}; - - void loadProfile(const std::string &filepath) - { - LOG_INFO("Load profile: %s", filepath.c_str()); - auto p = Profile(filepath); - if (p.getName() != std::string()) { - profiles.insert({p.getName(), std::move(p)}); - } - } - - std::vector getProfilesList(std::string ext) - { - std::vector profileFiles; - LOG_INFO("Scanning %s profiles folder: %s", ext.c_str(), profilesFolder); - auto dirList = vfs.listdir(profilesFolder, ext); - - for (vfs::DirectoryEntry ent : dirList) { - if (ent.attributes != vfs::FileAttributes::Directory) { - profileFiles.push_back(std::string(profilesFolder) + "/" + ent.fileName); - } - } - - LOG_INFO("Total number of profiles: %u", static_cast(profileFiles.size())); - return profileFiles; - } - - void init() - { - std::vector profileFiles = getProfilesList(".kprof"); - for (std::string mapName : profileFiles) { - if (std::size(mapName)) { - loadProfile(mapName); - } - } - if (std::size(profiles) == 0) { - LOG_ERROR("No keyboard profiles loaded"); - } - } - Profile empty; - - public: - static Profiles &get() - { - static Profiles *p; - if (p == nullptr) { - p = new Profiles(); - p->init(); - } - return *p; - } - - static Profile &get(const std::string &name) - { - // if profile not in profile map -> load - if (std::size(name) == 0) { - LOG_ERROR("Request for non existend profile: %s", name.c_str()); - return get().empty; - } - if (get().profiles.find(name) == get().profiles.end()) { - get().loadProfile(name); - } - return get().profiles[name]; - } - }; - uint32_t KeyInputMappedTranslation::handle(RawKey key, const std::string &keymap) { // get shortpress diff --git a/module-gui/gui/input/Translator.hpp b/module-gui/gui/input/Translator.hpp index 220105068..7a439f2ff 100644 --- a/module-gui/gui/input/Translator.hpp +++ b/module-gui/gui/input/Translator.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace gui { @@ -61,4 +62,75 @@ namespace gui } }; + /// profiles cache - load once for all + class Profiles + { + private: + const char *profilesFolder = "assets/profiles"; + std::map profiles = {}; + + void loadProfile(const std::string &filepath) + { + LOG_INFO("Load profile: %s", filepath.c_str()); + auto p = Profile(filepath); + if (p.getName() != std::string()) { + profiles.insert({p.getName(), std::move(p)}); + } + } + + std::vector getProfilesList(std::string ext) + { + std::vector profileFiles; + LOG_INFO("Scanning %s profiles folder: %s", ext.c_str(), profilesFolder); + auto dirList = vfs.listdir(profilesFolder, ext); + + for (vfs::DirectoryEntry ent : dirList) { + if (ent.attributes != vfs::FileAttributes::Directory) { + profileFiles.push_back(std::string(profilesFolder) + "/" + ent.fileName); + } + } + + LOG_INFO("Total number of profiles: %u", static_cast(profileFiles.size())); + return profileFiles; + } + + void init() + { + std::vector profileFiles = getProfilesList(".kprof"); + for (std::string mapName : profileFiles) { + if (std::size(mapName)) { + loadProfile(mapName); + } + } + if (std::size(profiles) == 0) { + LOG_ERROR("No keyboard profiles loaded"); + } + } + Profile empty; + + public: + static Profiles &get() + { + static Profiles *p; + if (p == nullptr) { + p = new Profiles(); + p->init(); + } + return *p; + } + + static Profile &get(const std::string &name) + { + // if profile not in profile map -> load + if (std::size(name) == 0) { + LOG_ERROR("Request for non existend profile: %s", name.c_str()); + return get().empty; + } + if (get().profiles.find(name) == get().profiles.end()) { + get().loadProfile(name); + } + return get().profiles[name]; + } + }; + } /* namespace gui */ diff --git a/module-gui/gui/widgets/CMakeLists.txt b/module-gui/gui/widgets/CMakeLists.txt index e16f1acb3..6615577b3 100644 --- a/module-gui/gui/widgets/CMakeLists.txt +++ b/module-gui/gui/widgets/CMakeLists.txt @@ -5,10 +5,10 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/Arc.cpp" "${CMAKE_CURRENT_LIST_DIR}/BottomBar.cpp" "${CMAKE_CURRENT_LIST_DIR}/CheckBox.cpp" - "${CMAKE_CURRENT_LIST_DIR}/Circle.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Circle.cpp" "${CMAKE_CURRENT_LIST_DIR}/Icon.cpp" "${CMAKE_CURRENT_LIST_DIR}/Image.cpp" - "${CMAKE_CURRENT_LIST_DIR}/ImageBox.cpp" + "${CMAKE_CURRENT_LIST_DIR}/ImageBox.cpp" "${CMAKE_CURRENT_LIST_DIR}/Item.cpp" "${CMAKE_CURRENT_LIST_DIR}/Label.cpp" "${CMAKE_CURRENT_LIST_DIR}/ListItem.cpp" @@ -36,6 +36,8 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/Style.cpp" "${CMAKE_CURRENT_LIST_DIR}/InputMode.cpp" "${CMAKE_CURRENT_LIST_DIR}/GridLayout.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Lines.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TextLineCursor.cpp" "${CMAKE_CURRENT_LIST_DIR}/RichTextParser.cpp" "${CMAKE_CURRENT_LIST_DIR}/TextFormat.cpp" "${CMAKE_CURRENT_LIST_DIR}/CheckBoxWithLabel.cpp" @@ -45,7 +47,8 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/BottomBar.hpp" "${CMAKE_CURRENT_LIST_DIR}/Circle.hpp" "${CMAKE_CURRENT_LIST_DIR}/Image.hpp" - "${CMAKE_CURRENT_LIST_DIR}/ImageBox.hpp" + "${CMAKE_CURRENT_LIST_DIR}/ImageBox.hpp" + "${CMAKE_CURRENT_LIST_DIR}/TextFixedSize.hpp" "${CMAKE_CURRENT_LIST_DIR}/Item.hpp" "${CMAKE_CURRENT_LIST_DIR}/Label.hpp" "${CMAKE_CURRENT_LIST_DIR}/ListItem.hpp" @@ -62,5 +65,8 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/TopBar.hpp" "${CMAKE_CURRENT_LIST_DIR}/Text.hpp" "${CMAKE_CURRENT_LIST_DIR}/CheckBoxWithLabel.hpp" + "${CMAKE_CURRENT_LIST_DIR}/CheckBoxWithLabel.hpp" + "${CMAKE_CURRENT_LIST_DIR}/Lines.hpp" + "${CMAKE_CURRENT_LIST_DIR}/TextLineCursor.hpp" ) diff --git a/module-gui/gui/widgets/Item.cpp b/module-gui/gui/widgets/Item.cpp index f175835e3..aa96a790c 100644 --- a/module-gui/gui/widgets/Item.cpp +++ b/module-gui/gui/widgets/Item.cpp @@ -274,7 +274,7 @@ namespace gui } } - Padding Item::getPadding() + Padding Item::getPadding() const { return padding; } diff --git a/module-gui/gui/widgets/Item.hpp b/module-gui/gui/widgets/Item.hpp index 6456722b5..38d56be69 100644 --- a/module-gui/gui/widgets/Item.hpp +++ b/module-gui/gui/widgets/Item.hpp @@ -45,8 +45,6 @@ namespace gui HBOX }; - NavigationDirection inputToNavigation(const InputEvent &evt); - class Item { private: @@ -224,7 +222,7 @@ namespace gui [[nodiscard]] Margins getMargins(); virtual void setPadding(const Padding &value); - [[nodiscard]] Padding getPadding(); + [[nodiscard]] Padding getPadding() const; virtual void setAlignment(const Alignment &value); [[nodiscard]] Alignment &getAlignment(); @@ -293,6 +291,7 @@ namespace gui virtual void clearNavigationItem(gui::NavigationDirection direction); Item(); + Item(Item &) = delete; virtual ~Item(); /// @defgroup inconsistent inconsistent size/offset accessors and setters @@ -353,4 +352,6 @@ namespace gui gui::Navigation *navigationDirections = nullptr; }; + NavigationDirection inputToNavigation(const InputEvent &evt); + } /* namespace gui */ diff --git a/module-gui/gui/widgets/Lines.cpp b/module-gui/gui/widgets/Lines.cpp new file mode 100644 index 000000000..801f5a208 --- /dev/null +++ b/module-gui/gui/widgets/Lines.cpp @@ -0,0 +1,167 @@ +#include "Lines.hpp" +#include "TextLineCursor.hpp" +#include "Text.hpp" + +namespace gui +{ + + // LEFT/RIGHT/UP/DOWN + auto Lines::processNavigation(TextLineCursor &cursor, const InputEvent &event) -> gui::InputBound + { + auto dir = inputToNavigation(event); + if (dir == NavigationDirection::NONE) { + return InputBound::UNDEFINED; + } + return canMove(cursor, dir); + } + + auto Lines::canMove(TextLineCursor &cursor, NavigationDirection dir) -> gui::InputBound + { + auto screen_bound = scroll_position + max_lines_count - 1; + auto lines_bound = lines.size() - 1; + + if (dir == NavigationDirection::UP || dir == NavigationDirection::LEFT) { + screen_bound = 0; + lines_bound = 0; + } + + if (dir == NavigationDirection::LEFT && cursor.getPosOnScreen() > 0) { + return InputBound::CAN_MOVE; + } + + unsigned int pos = cursor.BlockCursor::getPosition(); + auto textLine = getTextLine(cursor.getScreenLine()); + + if (textLine == nullptr) { + return InputBound::NO_DATA; + } + + size_t lineLength = textLine->length(); + + if (dir == NavigationDirection::RIGHT && pos < lineLength) { + return InputBound::CAN_MOVE; + } + + if (cursor.getScreenLine() >= lines_bound) { + return InputBound::NO_DATA; + } + + if (cursor.getScreenLine() >= screen_bound) { + return InputBound::HIT_BOUND; + } + + return InputBound::CAN_MOVE; + } + + gui::InputBound Lines::processAdding(TextLineCursor &cursor, const InputEvent &event) + { + auto keymap = parent->mode != nullptr ? parent->mode->get() : ""; + auto code = gui::Profiles::get(keymap).get(event.key.key_code, 0); + + if (code == KeyProfile::none_key && event.isShortPress()) { + return InputBound::CANT_PROCESS; + } + + auto format = cursor->getFormat(); + uint32_t line = cursor.getScreenLine(); + TextLine *textLine = getTextLine(line); + + if (textLine == nullptr || !cursor) { + return InputBound::CAN_ADD; + } + + auto bound = textLine->checkBounds(cursor, code, format); + if (bound == InputBound::CANT_PROCESS && line == scroll_position) { + return InputBound::CANT_PROCESS; + } + + return InputBound::CAN_ADD; + } + + auto Lines::processRemoval(TextLineCursor &cursor) -> gui::InputBound + { + if (lines.empty()) { + return InputBound::CANT_PROCESS; + } + + uint32_t line = cursor.getScreenLine(); + uint32_t pos = cursor.getPosOnScreen(); + + if (pos == 0) { + if (line == scroll_position + max_lines_count) { + return InputBound::HIT_BOUND; + } + if (line == 0) { + return InputBound::CANT_PROCESS; + } + } + + return InputBound::CAN_REMOVE; + } + + auto Lines::processTextInput(TextLineCursor &cursor, const InputEvent &event) -> gui::InputBound + { + if (!parent->isMode(EditMode::EDIT)) { + return gui::InputBound::CANT_PROCESS; + } + + if (event.isShortPress() && event.is(KeyCode::KEY_PND)) { + return processRemoval(cursor); + } + else { + return processAdding(cursor, event); + } + } + + void Lines::updateScrollPosition(NavigationDirection dir, uint32_t lines_to_scroll) + { + if (dir == NavigationDirection::UP) { + scroll_position -= lines_to_scroll; + } + + if (dir == NavigationDirection::DOWN) { + scroll_position += lines_to_scroll; + } + } + + void Lines::linesVAlign(Length parentSize) + { + for (auto &line : lines) { + line.alignV(parent->getAlignment(Axis::Y), parentSize, linesHeight()); + } + } + + void Lines::linesHAlign(Length parentSize) + { + for (auto &line : lines) { + line.alignH(parent->getAlignment(Axis::X), parentSize); + } + } + + auto Lines::checkBounds(TextLineCursor &cursor, InputEvent event) -> InputBound + { + auto bound = processNavigation(cursor, event); + + if (bound == InputBound::UNDEFINED) { + bound = processTextInput(cursor, event); + } + + return bound; + } + + void Lines::draw(TextCursor &cursor) + { + parent->drawLines(); + } + + TextLine *Lines::getTextLine(uint32_t line) + { + if (lines.empty() || line >= lines.size()) { + return nullptr; + } + + auto it = std::next(lines.begin(), line); + return &*it; + } // namespace gui + +} // namespace gui \ No newline at end of file diff --git a/module-gui/gui/widgets/Lines.hpp b/module-gui/gui/widgets/Lines.hpp new file mode 100644 index 000000000..ebbdf1256 --- /dev/null +++ b/module-gui/gui/widgets/Lines.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +#include "Alignment.hpp" +#include "InputEvent.hpp" +#include "TextConstants.hpp" +#include "utf8/UTF8.hpp" + +#include "InputMode.hpp" +#include "TextLine.hpp" +#include "Translator.hpp" + +namespace gui +{ + class Text; + class TextLineCursor; + + class Lines + { + Text *parent = nullptr; + std::list lines; + + uint32_t max_lines_count = 4; + uint32_t scroll_position = 0; + + public: + Lines(Text *parent) : parent(parent) + {} + + ~Lines() = default; + + void erase() + { + if (parent != nullptr) { + for (auto &line : lines) { + line.erase(); + } + } + lines.clear(); + } + + void emplace(TextLine &&line) + { + lines.emplace_back(std::move(line)); + } + + const auto &get() + { + return lines; + } + + auto &last() + { + return lines.back(); + } + + auto size() + { + return lines.size(); + } + + auto maxWidth() + { + unsigned int w = 0; + // could be std::max_element + for (auto &el : lines) { + w = el.width() > w ? el.width() : w; + } + return w; + } + + auto linesHeight() + { + unsigned int h = 0; + for (auto &el : lines) { + h += el.height(); + } + return h; + } + + void draw(TextCursor &cursor); + + void linesHAlign(Length parentSize); + void linesVAlign(Length parentSize); + auto checkBounds(TextLineCursor &cursor, InputEvent event) -> InputBound; + void updateScrollPosition(NavigationDirection scroll, uint32_t lines_to_scroll = 1); + + protected: + auto processTextInput(TextLineCursor &cursor, const InputEvent &event) -> InputBound; + auto processNavigation(TextLineCursor &cursor, const InputEvent &event) -> InputBound; + + auto canMove(TextLineCursor &cursor, NavigationDirection dir) -> InputBound; + InputBound processRemoval(TextLineCursor &cursor); + TextLine *getTextLine(uint32_t line); + InputBound processAdding(TextLineCursor &cursor, const InputEvent &event); + }; + +} // namespace gui \ No newline at end of file diff --git a/module-gui/gui/widgets/Text.cpp b/module-gui/gui/widgets/Text.cpp index d4ffbc357..925711ff2 100644 --- a/module-gui/gui/widgets/Text.cpp +++ b/module-gui/gui/widgets/Text.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "Lines.hpp" #if DEBUG_GUI_TEXT == 1 #define debug_text(...) LOG_DEBUG(__VA_ARGS__) @@ -39,6 +40,12 @@ namespace gui { + /// changes integer value to ascii int value (with no additional checks) + char intToAscii(int val) + { + return val + '0'; + } + Text::Text(Item *parent, const uint32_t &x, const uint32_t &y, @@ -47,9 +54,11 @@ namespace gui const UTF8 &text, ExpandMode expandMode, TextType textType) - : Rect(parent, x, y, w, h), lines(this), expandMode{expandMode}, textType{textType}, + : Rect(parent, x, y, w, h), expandMode{expandMode}, textType{textType}, format(FontManager::getInstance().getFont(style::window::font::small)) { + lines = std::make_unique(this); + alignment = style::text::defaultTextAlignment; setPenWidth(style::window::default_border_no_focus_w); @@ -95,9 +104,9 @@ namespace gui setText(std::make_unique(textToTextBlocks(text, format.getFont(), TextBlock::End::None))); } - void Text::setText(std::unique_ptr &&document) + void Text::setText(std::unique_ptr &&doc) { - buildDocument(std::move(document)); + buildDocument(std::move(doc)); onTextChanged(); } @@ -181,6 +190,12 @@ namespace gui buildCursor(); } + std::tuple scrollView(const TextCursor &cursor) + { + uint32_t scrolledLines = 1; + return {NavigationDirection::UP, scrolledLines}; + } + bool Text::onInput(const InputEvent &evt) { if (Rect::onInput(evt)) { @@ -203,24 +218,18 @@ namespace gui debug_text("handleActivation"); return true; } - if (handleNavigation(evt)) { - debug_text("handleNavigation"); - return true; - } - if (handleBackspace(evt)) { - debug_text("handleBackspace"); - return true; - } - if (handleAddChar(evt)) { - debug_text("handleAddChar"); - return true; - } - if (handleDigitLongPress(evt)) { - debug_text("handleDigitLongPress"); - return true; + + auto bound = lines->checkBounds(*cursor, evt); + bound = cursor->processBound(bound, evt); + + if (bound == InputBound::HIT_BOUND) { + auto [scroll, factor] = scrollView(*cursor); + lines->updateScrollPosition(scroll, factor); } - debug_text("not handled: %s", evt.str().c_str()); + if (bound != InputBound::CANT_PROCESS) { + return true; + } return false; } @@ -276,8 +285,8 @@ namespace gui if (isMode(EditMode::SCROLL) && (inputEvent.is(KeyCode::KEY_LEFT) || inputEvent.is(KeyCode::KEY_RIGHT))) { debug_text("Text in scroll mode ignores left/right navigation"); } - auto ret = cursor->move_cursor(inputToNavigation(inputEvent)); - debug_text("move_cursor: %s", c_str(ret)); + auto ret = cursor->moveCursor(inputToNavigation(inputEvent)); + debug_text("moveCursor: %s", c_str(ret)); if (ret == TextCursor::Move::Start || ret == TextCursor::Move::End) { debug_text("scrolling needs implementing"); return false; @@ -312,7 +321,7 @@ namespace gui void Text::drawLines() { - lines.erase(); + lines->erase(); auto sizeMinusPadding = [&](Axis axis, Area val) { auto size = area(val).size(axis); @@ -337,15 +346,16 @@ namespace gui Length h = sizeMinusPadding(Axis::Y, Area::Max); auto line_y_position = padding.top; - auto cursor = 0; + + BlockCursor draw_cursor(cursor->getDocument(), 0, 0, cursor->getFont()); debug_text("--> START drawLines: {%" PRIu32 ", %" PRIu32 "}", w, h); auto end = TextBlock::End::None; auto line_x_position = padding.left; do { - auto text_line = gui::TextLine(document.get(), cursor, w); - cursor += text_line.length(); + auto text_line = gui::TextLine(draw_cursor, w); + draw_cursor += text_line.length(); if (text_line.length() == 0 && end == TextBlock::End::None) { debug_text("cant show more text from this document"); @@ -364,13 +374,14 @@ namespace gui // for each different text which fits in line, addWidget last - to not trigger callbacks to parent // on resizes while not needed, after detach there can be no `break` othervise there will be leak - hence // detach - lines.emplace(std::move(text_line)); - auto &line = lines.last(); + lines->emplace(std::move(text_line)); + auto &line = lines->last(); line.setPosition(line_x_position, line_y_position); line.setParent(this); + line.alignH(getAlignment(Axis::X), w); - end = lines.last().getEnd(); + end = lines->last().getEnd(); line_y_position += line.height(); debug_text_lines("debug text drawing: \n start cursor: %d line length: %d end cursor %d : document length " @@ -394,9 +405,9 @@ namespace gui // should be done on each loop { uint16_t h_used = line_y_position + padding.bottom; - uint16_t w_used = lines.maxWidth() + padding.getSumInAxis(Axis::X); + uint16_t w_used = lines->maxWidth() + padding.getSumInAxis(Axis::X); - if (lines.size() == 0) { + if (lines->size() == 0) { debug_text("No lines to show, try to at least fit in cursor"); if (format.getFont() != nullptr && line_y_position < format.getFont()->info.line_height) { h_used = format.getFont()->info.line_height; @@ -414,8 +425,8 @@ namespace gui } } - lines.linesHAlign(sizeMinusPadding(Axis::X, Area::Normal)); - lines.linesVAlign(sizeMinusPadding(Axis::Y, Area::Normal)); + lines->linesHAlign(sizeMinusPadding(Axis::X, Area::Normal)); + lines->linesVAlign(sizeMinusPadding(Axis::Y, Area::Normal)); debug_text("<- END\n"); } @@ -458,7 +469,7 @@ namespace gui void Text::buildCursor() { erase(cursor); - cursor = new TextCursor(this, document.get()); + cursor = new TextLineCursor(this); cursor->setAlignment(this->getAlignment()); cursor->setMargins(this->getPadding()); showCursor(focus); @@ -544,12 +555,6 @@ namespace gui return false; } - /// changes integer value to ascii int value (with no additional checks) - char intToAscii(int val) - { - return val + '0'; - } - bool Text::handleDigitLongPress(const InputEvent &inputEvent) { if (!inputEvent.isLongPress()) { diff --git a/module-gui/gui/widgets/Text.hpp b/module-gui/gui/widgets/Text.hpp index b4e4838f5..b82f90665 100644 --- a/module-gui/gui/widgets/Text.hpp +++ b/module-gui/gui/widgets/Text.hpp @@ -13,13 +13,16 @@ #include "Label.hpp" #include "Rect.hpp" #include "Style.hpp" +#include "Lines.hpp" #include "TextCursor.hpp" #include "TextDocument.hpp" #include "TextLine.hpp" #include "Translator.hpp" +#include "TextLineCursor.hpp" namespace gui { + class Lines; /// @brief Widget that holds multiple lines of text. /// @@ -36,88 +39,15 @@ namespace gui class Text : public Rect { friend TextCursor; + friend Lines; protected: // holds list of labels for displaying currently visible text lines. - class Lines - { - Item *parent = nullptr; - std::list lines; - public: - void erase() - { - if (parent != nullptr) { - for (auto &line : lines) { - line.erase(); - } - } - lines.clear(); - } - - void emplace(TextLine &&line) - { - lines.emplace_back(std::move(line)); - } - - Lines(Item *parent) - { - this->parent = parent; - } - - const auto &get() - { - return lines; - } - - auto &last() - { - return lines.back(); - } - - auto size() - { - return lines.size(); - } - - auto maxWidth() - { - unsigned int w = 0; - // could be std::max_element - for (auto &el : lines) { - w = el.width() > w ? el.width() : w; - } - return w; - } - - auto linesHeight() - { - unsigned int h = 0; - for (auto &el : lines) { - h += el.height(); - } - return h; - } - - auto linesVAlign(Length parentSize) - { - for (auto &line : lines) { - line.alignV(parent->getAlignment(Axis::Y), parentSize, linesHeight()); - } - } - - auto linesHAlign(Length parentSize) - { - for (auto &line : lines) { - line.alignH(parent->getAlignment(Axis::X), parentSize); - } - } - - } lines; - - TextCursor *cursor = nullptr; + TextLineCursor *cursor = nullptr; std::unique_ptr document = std::make_unique(std::list()); InputMode *mode = nullptr; + std::unique_ptr lines = nullptr; void buildDocument(const UTF8 &text); void buildDocument(std::unique_ptr &&document); @@ -125,15 +55,15 @@ namespace gui /// show cursor if cursor should be visible void showCursor(bool focus); - EditMode editMode = EditMode::EDIT; + public: + ExpandMode expandMode = ExpandMode::EXPAND_NONE; + EditMode editMode = EditMode::EDIT; + KeyCode key_signs_remove = KeyCode::KEY_PND; + [[nodiscard]] bool isMode(EditMode edit) const { return editMode == edit; } - KeyCode key_signs_remove = KeyCode::KEY_PND; - - public: - ExpandMode expandMode = ExpandMode::EXPAND_NONE; protected: TextType textType = TextType::MULTI_LINE; @@ -141,11 +71,11 @@ namespace gui bool underline = false; TextFormat format; - bool moveCursor(const NavigationDirection &direction, std::unique_ptr &document); - bool handleNavigation(const InputEvent &inputEvent); - bool handleEnter(); + auto moveCursor(const NavigationDirection &direction, std::unique_ptr &document) -> bool; + auto handleNavigation(const InputEvent &inputEvent) -> bool; + auto handleEnter() -> bool; - std::list buildDrawList() override; + auto buildDrawList() -> std::list override; /// redrawing lines /// it redraws visible lines on screen and if needed requests resize in parent virtual void drawLines(); @@ -215,6 +145,8 @@ namespace gui private: gui::KeyInputMappedTranslation translator; + + public: TextChangedCallback textChangedCallback; bool handleRotateInputMode(const InputEvent &inputEvent); @@ -222,12 +154,15 @@ namespace gui bool handleSelectSpecialChar(const InputEvent &inputEvent); bool handleActivation(const InputEvent &inputEvent); bool handleBackspace(const InputEvent &inputEvent); - bool handleAddChar(const InputEvent &inputEvent); bool handleDigitLongPress(const InputEvent &inputEvent); + bool handleAddChar(const InputEvent &inputEvent); bool addChar(uint32_t utf8); bool removeChar(); + InputBound processBound(InputBound bound, const InputEvent &event); void onTextChanged(); }; + char intToAscii(int val); + } /* namespace gui */ diff --git a/module-gui/gui/widgets/TextBlock.cpp b/module-gui/gui/widgets/TextBlock.cpp index e0f4f6f6f..80c88988e 100644 --- a/module-gui/gui/widgets/TextBlock.cpp +++ b/module-gui/gui/widgets/TextBlock.cpp @@ -17,12 +17,14 @@ namespace gui if (getEnd() != End::Newline && eol == End::Newline) { this->text.insertCode(text::newline); } + end = eol; } TextBlock::TextBlock(const TextBlock &p) { text = p.text; format = std::make_unique(*p.format); + end = p.end; } const UTF8 &TextBlock::getText() const @@ -83,6 +85,7 @@ namespace gui if (getEnd() != End::Newline) { text.insertCode(text::newline); } + this->end = end; } void TextBlock::addChar(uint32_t utf_val, unsigned int pos) diff --git a/module-gui/gui/widgets/TextBlock.hpp b/module-gui/gui/widgets/TextBlock.hpp index edca35dbd..f627f62aa 100644 --- a/module-gui/gui/widgets/TextBlock.hpp +++ b/module-gui/gui/widgets/TextBlock.hpp @@ -26,7 +26,7 @@ namespace gui }; private: - End end = End::Newline; + End end = End::None; public: TextBlock(const UTF8 text, const RawFont *font, End eol = End::None); diff --git a/module-gui/gui/widgets/TextBlockCursor.cpp b/module-gui/gui/widgets/TextBlockCursor.cpp index dd1ac1199..894f8b106 100644 --- a/module-gui/gui/widgets/TextBlockCursor.cpp +++ b/module-gui/gui/widgets/TextBlockCursor.cpp @@ -3,9 +3,6 @@ #include "TextDocument.hpp" #include "TextParse.hpp" #include "log/log.hpp" -#include -#include -#include static const int last_char_inclusive = 0; // if then -1 / else 0 @@ -14,20 +11,20 @@ static const int last_char_inclusive = 0; // if then -1 / else 0 namespace gui { - auto BlockCursor::currentBlock() + auto BlockCursor::currentBlock() const { if (block_nr == text::npos) { - return std::end(document->blocks); + return document->blocks.end(); } return std::next(document->blocks.begin(), block_nr); } - auto BlockCursor::blocksEnd() + auto BlockCursor::blocksEnd() const { return std::end(document->blocks); } - auto BlockCursor::blocksBegin() + auto BlockCursor::blocksBegin() const { return std::begin(document->blocks); } @@ -73,10 +70,28 @@ namespace gui ; } + auto BlockCursor::atEol() const -> bool + { + if (!checkDocument() || checkNpos()) { + return false; + } + + auto block = currentBlock(); + // check if we have jumped to new block because of new line + if (pos == 0 && block != blocksBegin() && block_jump) { + return (--block)->getEnd() == TextBlock::End::Newline; + } + + return pos == block->length() && block->getEnd() == TextBlock::End::Newline; + } + auto BlockCursor::operator+=(unsigned int val) -> BlockCursor & { // just use operator ++ - for (unsigned int i = 0; i < val; ++i, ++*this) {} + for (unsigned int i = 0; i < val; i++) { + ++*this; + } + return *this; } @@ -85,23 +100,23 @@ namespace gui if (!checkDocument()) { return *this; } - else { - } resetNpos(); auto block = std::next(document->blocks.begin(), block_nr); - bool end_of_current_block_reached = - pos >= std::next(document->blocks.begin(), block_nr)->length() + - (block->getEnd() != TextBlock::End::Newline ? last_char_inclusive : -1); - ; - bool last_document_reached = block_nr + 1 == document->blocks.size(); + size_t block_size = std::next(document->blocks.begin(), block_nr)->length(); + block_size += (block->getEnd() != TextBlock::End::Newline ? last_char_inclusive : -1); + + bool end_of_current_block_reached = pos >= block_size; + bool last_document_reached = block_nr + 1 == document->blocks.size(); if (end_of_current_block_reached && last_document_reached) { return *this; } + block_jump = end_of_current_block_reached; + if (end_of_current_block_reached) { block_nr += 1; pos = 0; @@ -109,6 +124,7 @@ namespace gui } pos += 1; + return *this; } @@ -133,9 +149,8 @@ namespace gui if (pos == 0) { block_nr -= 1; - auto block = std::next(document->blocks.begin(), block_nr); - pos = block->length() + last_char_inclusive; - if (block->getEnd() == TextBlock::End::Newline) { + pos = currentBlock()->length() + last_char_inclusive; + if (atEol()) { pos -= 1; } return *this; @@ -164,8 +179,7 @@ namespace gui return; } document->append(TextBlock("", default_font, TextBlock::End::None)); - block_nr = 0; - pos = 0; + resetNpos(); } auto block = currentBlock(); if (block == blocksEnd()) { @@ -185,8 +199,7 @@ namespace gui return; } if (document->isEmpty()) { - block_nr = 0; - pos = 0; + resetNpos(); } document->append(std::move(textblock)); } @@ -236,6 +249,32 @@ namespace gui return *currentBlock(); } + auto BlockCursor::getText() -> std::string + { + if (currentBlock() == blocksEnd()) { + return ""; + } + return currentBlock()->getText(getPosition()); + } + + auto BlockCursor::getUTF8Text() -> UTF8 + { + if (currentBlock() == blocksEnd()) { + return ""; + } + return currentBlock()->getText(getPosition()); + } + + const TextBlock *BlockCursor::operator->() + { + return &*currentBlock(); + } + + void BlockCursor::resetJumps() + { + block_jump = false; + } + auto BlockCursor::begin() -> std::list::iterator { return document == nullptr ? document->blocks.end() : document->blocks.begin(); diff --git a/module-gui/gui/widgets/TextBlockCursor.hpp b/module-gui/gui/widgets/TextBlockCursor.hpp index 230ccf55c..ef9c72efb 100644 --- a/module-gui/gui/widgets/TextBlockCursor.hpp +++ b/module-gui/gui/widgets/TextBlockCursor.hpp @@ -1,8 +1,10 @@ #pragma once +#include "utf8/UTF8.hpp" #include "TextConstants.hpp" #include #include +#include #include namespace gui @@ -23,14 +25,15 @@ namespace gui { protected: TextDocument *document = nullptr; - auto currentBlock(); - auto blocksEnd(); - auto blocksBegin(); + auto currentBlock() const; + auto blocksEnd() const; + auto blocksBegin() const; RawFont *default_font = nullptr; private: unsigned int pos = text::npos; unsigned int block_nr = text::npos; + bool block_jump = false; protected: [[nodiscard]] auto checkNpos() const -> bool; @@ -49,6 +52,11 @@ namespace gui return pos; } + [[nodiscard]] auto getDocument() const -> TextDocument * + { + return document; + } + [[nodiscard]] auto getBlockNr() const -> unsigned int { return block_nr; @@ -64,6 +72,8 @@ namespace gui [[nodiscard]] auto atEnd() const -> bool; + [[nodiscard]] auto atEol() const -> bool; + operator bool() const { return !checkNpos(); @@ -73,11 +83,18 @@ namespace gui auto operator++() -> BlockCursor &; auto operator-=(unsigned int) -> BlockCursor &; auto operator--() -> BlockCursor &; + + // return if handled ( this is not i.e. at begin/end) + auto removeChar() -> bool; + auto operator*() -> const TextBlock &; + auto operator->() -> const TextBlock *; + void addChar(uint32_t utf_val); void addTextBlock(TextBlock &&); - // return if handled ( this is not i.e. at begin/end) - bool removeChar(); - const TextBlock &operator*(); + + [[nodiscard]] auto getText() -> std::string; + auto getUTF8Text() -> UTF8; + void resetJumps(); /// iterable /// { diff --git a/module-gui/gui/widgets/TextConstants.hpp b/module-gui/gui/widgets/TextConstants.hpp index 6b937f257..7c11d329c 100644 --- a/module-gui/gui/widgets/TextConstants.hpp +++ b/module-gui/gui/widgets/TextConstants.hpp @@ -29,4 +29,18 @@ namespace gui SINGLE_LINE = 1, MULTI_LINE }; + + enum class InputBound + { + UNDEFINED = 0x000, + + CANT_PROCESS = 0x100, + NO_DATA = 0x101, + HIT_BOUND = 0x110, + + CAN_ADD = 0x1000, + CAN_MOVE = 0x10000, + CAN_REMOVE = 0x100000 + }; + } // namespace gui diff --git a/module-gui/gui/widgets/TextCursor.cpp b/module-gui/gui/widgets/TextCursor.cpp index 2734a8861..6bbc4dbd8 100644 --- a/module-gui/gui/widgets/TextCursor.cpp +++ b/module-gui/gui/widgets/TextCursor.cpp @@ -15,18 +15,19 @@ namespace gui { const unsigned int TextCursor::default_width = 2; - TextCursor::TextCursor(gui::Text *parent, gui::TextDocument *document) + TextCursor::TextCursor(gui::Text *parent, unsigned int pos, unsigned int block) : Rect(parent, 0, 0, default_width, 1), - BlockCursor( - document, text::npos, text::npos, parent != nullptr ? parent->getTextFormat().getFont() : nullptr), + BlockCursor(parent != nullptr ? parent->document.get() : nullptr, + pos, + block, + parent != nullptr ? parent->getTextFormat().getFont() : nullptr), text(parent) { setFilled(true); setVisible(false); - pos_on_screen = document->getText().length(); } - TextCursor::Move TextCursor::move_cursor(NavigationDirection direction) + TextCursor::Move TextCursor::moveCursor(NavigationDirection direction) { debug_text_cursor("cursor: screen pos: %d block: %d pos: %d %s", pos_on_screen, @@ -55,6 +56,7 @@ namespace gui auto block = document->getBlock(this); auto len = block->getText().length(); pos_on_screen = len - 1; + return Move::Up; } return Move::InLine; } @@ -64,6 +66,10 @@ namespace gui if (nr == getBlockNr() || nr == text::npos) { ++pos_on_screen; } + else { + pos_on_screen = 0; + return Move::Down; + } return Move::InLine; } @@ -89,6 +95,7 @@ namespace gui return Move::Down; } + return Move::Error; } @@ -103,7 +110,7 @@ namespace gui auto block = getBlockNr(); - for (auto &line : text->lines.get()) { + for (auto &line : text->lines->get()) { if (line.getBlockNr() == block) { if (offset_pos + line.length() >= pos_on_screen) { auto column = pos_on_screen - offset_pos; @@ -131,7 +138,7 @@ namespace gui x = getAxisAlignmentValue(Axis::X, w); y = getAxisAlignmentValue(Axis::Y, h); } - else if (text != nullptr || text->lines.size() > 0) { + else if (text != nullptr || text->lines->size() > 0) { auto [line, column, row] = getLine(); if (line == nullptr || column == text::npos || row == text::npos) { setArea({x, y, w, h}); @@ -150,10 +157,10 @@ namespace gui { BlockCursor::addChar(utf_val); if (utf_val == text::newline) { - move_cursor(NavigationDirection::DOWN); + moveCursor(NavigationDirection::DOWN); } else { - move_cursor(NavigationDirection::RIGHT); + moveCursor(NavigationDirection::RIGHT); } } @@ -171,16 +178,42 @@ namespace gui BlockCursor::addTextBlock(std::move(textblock)); // +1 is for block barier for (unsigned int i = 0; i < len + 1; ++i) { - move_cursor(NavigationDirection::RIGHT); + moveCursor(NavigationDirection::RIGHT); } return *this; } void TextCursor::removeChar() { - move_cursor(NavigationDirection::LEFT); + moveCursor(NavigationDirection::LEFT); BlockCursor::removeChar(); } + + InputBound TextCursor::processBound(InputBound bound, const InputEvent &event) + { + if (bound == InputBound::CAN_MOVE) { + moveCursor(inputToNavigation(event)); + } + + if (bound == InputBound::CAN_REMOVE) { + removeChar(); + } + + if (bound == InputBound::CAN_ADD) { + if (event.isLongPress()) { + auto val = toNumeric(event.keyCode); + if (val != InvalidNumericKeyCode) { + addChar(intToAscii(val)); + } + } + else { + text->handleAddChar(event); + } + } + + return bound; + } + } // namespace gui const char *c_str(enum gui::TextCursor::Move what) diff --git a/module-gui/gui/widgets/TextCursor.hpp b/module-gui/gui/widgets/TextCursor.hpp index d9d27cade..e230d6e15 100644 --- a/module-gui/gui/widgets/TextCursor.hpp +++ b/module-gui/gui/widgets/TextCursor.hpp @@ -8,7 +8,6 @@ namespace gui { - class Text; class TextDocument; @@ -34,7 +33,7 @@ namespace gui Error, /// error - now not implemented }; - TextCursor(gui::Text *parent, gui::TextDocument *document); + TextCursor(gui::Text *parent, unsigned int pos = 0, unsigned int block = 0); TextCursor() = delete; /// Up Down - end of line movement like in vi @@ -43,7 +42,7 @@ namespace gui /// - informs that we changed line when needed - TODO think about it better... ( and if it's needed...?) /// - with_update - updates position in parent ( if false not - means we handled it already with i.e. addChar or /// removeChar) - Move move_cursor(NavigationDirection direction); + virtual Move moveCursor(NavigationDirection direction); void reset(); // TODO note to self - here should be too UTF8 char handling, not in document... @@ -57,6 +56,13 @@ namespace gui TextCursor &operator<<(const UTF8 &); TextCursor &operator<<(TextBlock); void removeChar(); + + auto getPosOnScreen() const + { + return pos_on_screen; + } + + auto processBound(InputBound bound, const InputEvent &event) -> InputBound; }; } // namespace gui diff --git a/module-gui/gui/widgets/TextDocument.cpp b/module-gui/gui/widgets/TextDocument.cpp index 0213d5922..11416f5b4 100644 --- a/module-gui/gui/widgets/TextDocument.cpp +++ b/module-gui/gui/widgets/TextDocument.cpp @@ -1,5 +1,4 @@ #include "TextDocument.hpp" -#include "log/log.hpp" #include #include #include @@ -22,9 +21,9 @@ namespace gui blocks.clear(); } - void TextDocument::append(std::list &&blocks) + void TextDocument::append(std::list &&block_list) { - for (auto &&el : blocks) { + for (auto &&el : block_list) { this->blocks.emplace_back(std::move(el)); } } @@ -41,7 +40,7 @@ namespace gui l_block.setEnd(eol); } - UTF8 TextDocument::getText() const + auto TextDocument::getText() const -> UTF8 { UTF8 output; if (blocks.size() != 0) { @@ -53,7 +52,7 @@ namespace gui return output; } - BlockCursor TextDocument::getBlockCursor(unsigned int position) + auto TextDocument::getBlockCursor(unsigned int position) -> BlockCursor { unsigned int block_no = 0; unsigned int loop_position = 0; @@ -77,21 +76,20 @@ namespace gui return BlockCursor(); } - TextPart TextDocument::getTextPart(BlockCursor cursor) + auto TextDocument::getText(BlockCursor cursor) -> std::string { if (cursor) { - auto block = std::next(blocks.begin(), cursor.getBlockNr()); - return TextPart(cursor, block->getText(cursor.getPosition()), block->getFormat()->getFont()); + return cursor.getText(); } - return TextPart(); + return ""; } - [[nodiscard]] const std::list &TextDocument::getBlocks() const + [[nodiscard]] auto TextDocument::getBlocks() const -> const std::list & { return blocks; } - [[nodiscard]] const TextBlock *TextDocument::getBlock(BlockCursor *cursor) const + [[nodiscard]] auto TextDocument::getBlock(BlockCursor *cursor) const -> const TextBlock * { if (cursor != nullptr && *cursor) { return &operator()(*cursor); @@ -99,7 +97,7 @@ namespace gui return nullptr; } - const TextBlock &TextDocument::operator()(const BlockCursor &cursor) const + auto TextDocument::operator()(const BlockCursor &cursor) const -> const TextBlock & { assert(cursor.getBlockNr() < blocks.size()); return *std::next(blocks.begin(), cursor.getBlockNr()); diff --git a/module-gui/gui/widgets/TextDocument.hpp b/module-gui/gui/widgets/TextDocument.hpp index ac06ea5c8..d03d2abd6 100644 --- a/module-gui/gui/widgets/TextDocument.hpp +++ b/module-gui/gui/widgets/TextDocument.hpp @@ -9,17 +9,6 @@ namespace gui { class TextLine; - /// based on TextBlock - which is paragraph representation - struct TextPart - { - TextPart(BlockCursor cursor, UTF8 text, RawFont *font) : cursor(cursor), text(std::move(text)), font(font) - {} - TextPart() = default; - BlockCursor cursor; - UTF8 text; - RawFont *font = nullptr; - }; - class TextDocument { friend BlockCursor; @@ -40,7 +29,7 @@ namespace gui /// --- in progress BlockCursor getBlockCursor(unsigned int position); /// get part of TextBlock based on cursor - TextPart getTextPart(BlockCursor cursor); + std::string getText(BlockCursor cursor); /// needed for tests, alternatively could be mocked in test... [[nodiscard]] const std::list &getBlocks() const; diff --git a/module-gui/gui/widgets/TextFixedSize.cpp b/module-gui/gui/widgets/TextFixedSize.cpp index 3515a5723..fb10474f1 100644 --- a/module-gui/gui/widgets/TextFixedSize.cpp +++ b/module-gui/gui/widgets/TextFixedSize.cpp @@ -27,8 +27,7 @@ namespace gui void TextFixedSize::drawLines() { - - lines.erase(); + lines->erase(); auto sizeMinusPadding = [&](Axis axis, Area val) { auto size = area(val).size(axis); @@ -44,16 +43,17 @@ namespace gui uint32_t w = sizeMinusPadding(Axis::X, Area::Normal); uint32_t h = sizeMinusPadding(Axis::Y, Area::Normal); auto line_y_position = padding.top; - auto cursor = 0; + + BlockCursor draw_cursor(cursor->getDocument(), 0, 0, cursor->getFont()); unsigned int currentLine = 0; unsigned int lineHeight = format.getFont()->info.line_height + underlinePadding; auto line_x_position = padding.left; do { - auto text_line = gui::TextLine( - document.get(), cursor, w, lineHeight, underline, UnderlineDrawMode::WholeLine, underlinePadding); - cursor += text_line.length(); + auto text_line = + gui::TextLine(draw_cursor, w, lineHeight, underline, UnderlineDrawMode::WholeLine, underlinePadding); + draw_cursor += text_line.length(); if (text_line.height() > 0 && lineHeight != text_line.height()) { lineHeight = text_line.height(); @@ -67,8 +67,8 @@ namespace gui break; } - lines.emplace(std::move(text_line)); - auto &line = lines.last(); + lines->emplace(std::move(text_line)); + auto &line = lines->last(); line.setPosition(line_x_position, line_y_position); line.setParent(this); @@ -78,7 +78,7 @@ namespace gui } while (true); - lines.linesHAlign(sizeMinusPadding(Axis::X, Area::Normal)); - lines.linesVAlign(sizeMinusPadding(Axis::Y, Area::Normal)); + lines->linesHAlign(sizeMinusPadding(Axis::X, Area::Normal)); + lines->linesVAlign(sizeMinusPadding(Axis::Y, Area::Normal)); } } // namespace gui diff --git a/module-gui/gui/widgets/TextLine.cpp b/module-gui/gui/widgets/TextLine.cpp index 2b058e8cb..5c2cfa418 100644 --- a/module-gui/gui/widgets/TextLine.cpp +++ b/module-gui/gui/widgets/TextLine.cpp @@ -3,8 +3,10 @@ #include "Label.hpp" #include "TextBlock.hpp" #include "TextDocument.hpp" +#include "TextCursor.hpp" #include #include +#include namespace gui { @@ -23,34 +25,39 @@ namespace gui /// Note - line breaking could be done here with different TextLines to return /// or via different block types (i.e. numeric block tyle could be not "breakable" - TextLine::TextLine(TextDocument *document, unsigned int start_position, unsigned int max_width) + TextLine::TextLine(const BlockCursor &cursor, unsigned int max_width) : max_width(max_width) { - if (document == nullptr) { - return; - } - - auto cursor = document->getBlockCursor(start_position); - auto old_cursor = cursor; + BlockCursor localCursor = cursor; + localCursor.resetJumps(); do { - if (!cursor) { // cursor is faulty - break; - } - if (old_cursor.getBlockNr() != cursor.getBlockNr() && - (*document)(old_cursor).getEnd() == TextBlock::End::Newline) { - end = TextBlock::End::Newline; - break; + if (!localCursor) { // cursor is faulty + return; } - // it would be better if cursor would know what to do when it jumps over blocks + if (localCursor.atEnd()) { + return; + } - // take Part of TextBlock we want to show - auto text_part = document->getTextPart(cursor); - auto text_format = (*cursor).getFormat(); + if (localCursor.atEol()) { + width_used = max_width; + return; + } + + // take text we want to show + auto text = localCursor.getUTF8Text(); + + if (text.length() == 0) { + ++localCursor; + continue; + } + + auto text_format = localCursor->getFormat(); if (text_format->getFont() == nullptr) { break; } - auto can_show = text_format->getFont()->getCharCountInSpace(text_part.text, max_width - width_used); + + auto can_show = text_format->getFont()->getCharCountInSpace(text, max_width - width_used); // we can show nothing - this is the end of this line if (can_show == 0) { @@ -64,7 +71,7 @@ namespace gui } // create item for show and update Line data - auto item = buildUITextPart(text_part.text.substr(0, can_show), text_format); + auto item = buildUITextPart(text.substr(0, can_show), text_format); number_letters_shown += can_show; width_used += item->getTextNeedSpace(); height_used = std::max(height_used, item->getTextHeight()); @@ -73,31 +80,14 @@ namespace gui block_nr = cursor.getBlockNr(); // not whole text shown, try again for next line if you want - if (can_show < text_part.text.length()) { + if (can_show < text.length()) { break; } - old_cursor = cursor; - cursor = document->getBlockCursor(start_position + number_letters_shown); + localCursor += can_show; } while (true); } - TextLine::TextLine(TextDocument *document, - unsigned int start_position, - unsigned int max_width, - unsigned int init_height, - bool drawUnderline, - UnderlineDrawMode drawUnderlineMode, - Position underlinePadding) - : TextLine(document, start_position, max_width) - { - this->drawUnderline = drawUnderline; - this->drawUnderlineMode = drawUnderlineMode; - this->underlinePadding = underlinePadding; - - createUnderline(max_width, init_height); - } - TextLine::TextLine(TextLine &&from) { elements_to_show_in_line = std::move(from.elements_to_show_in_line); @@ -110,6 +100,7 @@ namespace gui underlinePadding = from.underlinePadding; end = from.end; block_nr = from.block_nr; + max_width = from.max_width; } TextLine::~TextLine() @@ -205,6 +196,19 @@ namespace gui return width; } + UTF8 TextLine::getText(unsigned int pos) const + { + UTF8 text; + for (auto &label : elements_to_show_in_line) { + if (label->getFont() == nullptr) { + continue; + } + text += label->getText(); + } + + return text; + } + void TextLine::erase() { for (auto &el : elements_to_show_in_line) { @@ -260,7 +264,7 @@ namespace gui } } - void TextLine::createUnderline(unsigned int max_width, unsigned int init_height) + void TextLine::createUnderline(unsigned int width, unsigned int init_height) { if (drawUnderline) { @@ -283,4 +287,16 @@ namespace gui } } + auto TextLine::checkBounds(TextLineCursor &cursor, uint32_t utf_value, const TextFormat *format) -> InputBound + { + auto font = format->getFont(); + auto text = getText(0); + text.insertCode(utf_value); + + if ((width_used + font->getPixelWidth(text)) <= max_width) { + return InputBound::CAN_ADD; + } + + return InputBound::CANT_PROCESS; + } } // namespace gui diff --git a/module-gui/gui/widgets/TextLine.hpp b/module-gui/gui/widgets/TextLine.hpp index 274c4da7c..067b281c1 100644 --- a/module-gui/gui/widgets/TextLine.hpp +++ b/module-gui/gui/widgets/TextLine.hpp @@ -5,9 +5,12 @@ #include "Label.hpp" #include "TextDocument.hpp" #include "BoxLayout.hpp" +#include "TextLineCursor.hpp" namespace gui { + class TextCursor; + enum class UnderlineDrawMode { WholeLine, @@ -20,6 +23,7 @@ namespace gui unsigned int number_letters_shown = 0; Length width_used = 0; Length height_used = 0; + Length max_width = 0; std::list