diff --git a/module-apps/application-settings/windows/UITestWindow.cpp b/module-apps/application-settings/windows/UITestWindow.cpp index fbe5ed5e2..be0f07503 100644 --- a/module-apps/application-settings/windows/UITestWindow.cpp +++ b/module-apps/application-settings/windows/UITestWindow.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace gui { @@ -30,8 +31,29 @@ UiTestWindow::UiTestWindow(app::Application *app) : AppWindow(app, "TEST_UI") application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST); bottomBar->getText(BottomBar::Side::CENTER); })); + text->setVisible(false); + auto box = new GridLayout(0, title->offset_h(), style::window_width, bottomBar->offset_h() - title->offset_h(), {90,90} ); + for(auto schar : gui::special_chars) { + auto el = new gui::Label(box, 0, 0, 80, 80); + style::window::decorate(el); + el->setAlignement( gui::Alignment(gui::Alignment::ALIGN_HORIZONTAL_CENTER, gui::Alignment::ALIGN_VERTICAL_CENTER)); + el->setText(std::string(1,schar)); + el->setFont(style::window::font::medium); + el->activatedCallback = [=](Item& it) { + LOG_INFO("handled %s", el->getText().c_str()); return true; + }; + // on click -> send onInput event with mapped & special keypress (...) + // better - read special_keys_keymap and handle accordingly ... to be done + } + box->setVisible(true); + box->setNavigation(nullptr,nullptr); + /// TODO do not resize on each addWidget .. just once :( + /// TODO shouldn't be needed here + /// TODO add widget -> put widget in it's position + box->resizeItems(); + addWidget(box); // TODO TODO attach(cb - show special characters, && input somehow) - setFocusItem(text); + setFocusItem(box->getNavigationItem()); } void UiTestWindow::rebuild() { LOG_INFO("Only if I have to!"); } diff --git a/module-gui/gui/widgets/BoxLayout.cpp b/module-gui/gui/widgets/BoxLayout.cpp index 1d5b84f70..205f0cd74 100644 --- a/module-gui/gui/widgets/BoxLayout.cpp +++ b/module-gui/gui/widgets/BoxLayout.cpp @@ -16,9 +16,6 @@ BoxLayout::BoxLayout( Item* parent, const uint32_t& x, const uint32_t& y, const Rect ( parent, x, y, w, h ){ } -BoxLayout::~BoxLayout() { -} - void BoxLayout::resizeItems() { } diff --git a/module-gui/gui/widgets/BoxLayout.hpp b/module-gui/gui/widgets/BoxLayout.hpp index fcce03243..9fc90681e 100644 --- a/module-gui/gui/widgets/BoxLayout.hpp +++ b/module-gui/gui/widgets/BoxLayout.hpp @@ -28,7 +28,7 @@ protected: public: BoxLayout(); BoxLayout( Item* parent, const uint32_t& x, const uint32_t& y, const uint32_t& w, const uint32_t& h); - ~BoxLayout(); + virtual ~BoxLayout() = default; //virtual methods from Item void setPosition( const short& x, const short& y ) override; @@ -44,7 +44,7 @@ protected: public: HBox(); HBox( Item* parent, const uint32_t& x, const uint32_t& y, const uint32_t& w, const uint32_t& h); - ~HBox() {}; + virtual ~HBox() = default; }; class VBox : public BoxLayout { @@ -52,7 +52,7 @@ public: void resizeItems() override; VBox(); VBox( Item* parent, const uint32_t& x, const uint32_t& y, const uint32_t& w, const uint32_t& h); - ~VBox() {}; + virtual ~VBox() = default; }; } /* namespace gui */ diff --git a/module-gui/gui/widgets/CMakeLists.txt b/module-gui/gui/widgets/CMakeLists.txt index 55f37e2c0..abb8a0422 100644 --- a/module-gui/gui/widgets/CMakeLists.txt +++ b/module-gui/gui/widgets/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/Text.cpp" "${CMAKE_CURRENT_LIST_DIR}/Style.cpp" "${CMAKE_CURRENT_LIST_DIR}/InputMode.cpp" + "${CMAKE_CURRENT_LIST_DIR}/GridLayout.cpp" PUBLIC "${CMAKE_CURRENT_LIST_DIR}/Alignment.hpp" "${CMAKE_CURRENT_LIST_DIR}/BottomBar.hpp" diff --git a/module-gui/gui/widgets/GridLayout.cpp b/module-gui/gui/widgets/GridLayout.cpp new file mode 100644 index 000000000..f77cfe34a --- /dev/null +++ b/module-gui/gui/widgets/GridLayout.cpp @@ -0,0 +1,110 @@ +#include "GridLayout.hpp" +#include +#include "Style.hpp" + +using namespace gui; + +GridLayout::GridLayout(Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h, GridSize grid) + : BoxLayout(parent, x, y, w, h), grid(grid) +{ + setPenWidth(style::window::default_border_no_focus_w); + setPenFocusWidth(style::window::default_border_focucs_w); +} + +void GridLayout::resizeItems() +{ + + if( grid.x == 0 || grid.y == 0 ) { + LOG_ERROR("Grid == 0 - abort"); + return; + } + uint32_t el_in_x = widgetArea.w / grid.x; + uint32_t el_in_y = widgetArea.h / grid.y; + uint32_t strech_x = 0; + uint32_t strech_y = 0; + uint32_t max_elements = el_in_x * el_in_y; + + if (children.size() > max_elements) + { + LOG_ERROR("More children than possible to show"); + } + if (strech) { + strech_x = (widgetArea.w - grid.x*el_in_x )/el_in_x; + strech_y = (widgetArea.h - grid.y*el_in_y )/el_in_y; + } + + int i = 0; + int row = 0; + int col = 0; + for (auto it = children.begin(); it != children.end() && i <= max_elements; ++it, ++i) + { + // check if element will fit in + if ((*it)->widgetArea.w > grid.x || (*it)->widgetArea.h > grid.y) + { + LOG_ERROR("Element %d too big", i); + } + // set position + (*it)->setPosition(col * grid.x + strech_x, row * grid.y + strech_y); + // shift row/col + ++col; + if (col == el_in_y) + { + col = 0; + ++row; + } + } +} + +// TODO commomize - move loop to lambda +void GridLayout::setNavigation(Item *previous, Item *next) +{ + uint32_t el_in_x = widgetArea.w / grid.x; + uint32_t el_in_y = widgetArea.h / grid.y; + uint32_t max_elements = el_in_x * el_in_y; + int i = 0; + int row = 0; + int col = 0; + if (children.size() == 0) + { + LOG_ERROR("No children to set navigation"); + } + for (auto it = children.begin(); it != children.end() && i < max_elements; ++it, ++i) + { + if (i != 0) + { + (*it)->setNavigationItem(NavigationDirection::LEFT, *std::prev(it)); + } + if (col + 1 % el_in_x != 0 && i + 1 < children.size()) + { + LOG_INFO("Set next navigation for item %d", i); + (*it)->setNavigationItem(NavigationDirection::RIGHT, *std::next(it)); + } + if (row != 0) + { + (*it)->setNavigationItem(NavigationDirection::UP, *std::prev(it, el_in_x)); + } + if (row + 1 % el_in_y != 0 && i + el_in_x < children.size()) + { + (*it)->setNavigationItem(NavigationDirection::DOWN, *std::next(it, el_in_x)); + } + + ++col; + if (col == el_in_y) + { + col = 0; + ++row; + } + } + // TODO + // children.front()->setNavigationItem(NavigationDirection::UP | NavigationDirection::LEFT, previous); + // auto last_element = (*std::next(children.begin(), max_elements < children.size() ? max_elements - 1 : children.size())); + // last_element->setNavigationItem(NavigationDirection::DOWN | NavigationDirection::RIGHT, next); +} + +bool GridLayout::onFocus(bool state) +{ + if(children.size()) { + children.front()->setFocus(state); + } + return true; +} diff --git a/module-gui/gui/widgets/GridLayout.hpp b/module-gui/gui/widgets/GridLayout.hpp new file mode 100644 index 000000000..f6cb3ddf4 --- /dev/null +++ b/module-gui/gui/widgets/GridLayout.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "BoxLayout.hpp" + +namespace gui +{ + +class GridLayout : public BoxLayout +{ + protected: + + struct GridSize { + uint32_t x = 0; + uint32_t y = 0; + } grid; + + public: + // one defined + GridLayout(Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h, GridSize grid); + // derived + GridLayout(const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h, GridSize grid) : GridLayout(nullptr, x,y,w,h, grid) {} + GridLayout() : GridLayout(0,0,0,0,{0,0}) {} + virtual ~GridLayout() = default; + // shall strech elements on resize? + bool strech = true; + virtual void resizeItems() override; + +// virtual methods from Item - use from parent +// void setPosition(const short &x, const short &y) override; +// void setSize(const short &w, const short &h) override; +// bool addWidget(gui::Item *item) override; +// bool removeWidget(Item *item) override; +// std::list buildDrawList() override; + /// set internal navigation and previous/next item to show when reached the end + void setNavigation(Item* previous, Item* next); + Item* getNavigationItem() { return children.size()?*children.begin():nullptr; } + virtual bool onFocus(bool state) override; +}; + +}; // namespace gui diff --git a/module-gui/gui/widgets/InputMode.hpp b/module-gui/gui/widgets/InputMode.hpp index 9510b9aa1..ef77cc6e6 100644 --- a/module-gui/gui/widgets/InputMode.hpp +++ b/module-gui/gui/widgets/InputMode.hpp @@ -4,6 +4,12 @@ #include #include +// TODO read from keymap file ... +namespace gui +{ +inline const std::vector special_chars = {'.', ',', '_', ':', ';', ')', '(', '?', '!', '/', '*', '+'}; +} + /// this widget has one goal - nicelly change input parsing which is done in application in it's widgets class InputMode { diff --git a/module-gui/gui/widgets/Rect.cpp b/module-gui/gui/widgets/Rect.cpp index 85ced71f5..e5edba259 100644 --- a/module-gui/gui/widgets/Rect.cpp +++ b/module-gui/gui/widgets/Rect.cpp @@ -23,25 +23,22 @@ Rect::Rect() : corners{RectangleCornerFlags::GUI_RECT_ALL_CORNERS} { } -Rect::Rect( Item* parent, const uint32_t& x, const uint32_t& y, const uint32_t& w, const uint32_t& h) : - borderColor(Color(0,0)), - fillColor(Color(15,15)), - penWidth{1}, - penFocusWidth{1}, - filled{false}, - edges{RectangleEdgeFlags::GUI_RECT_ALL_EDGES}, - flatEdges{ RectangleFlatFlags::GUI_RECT_FLAT_NO_FLAT}, - corners{RectangleCornerFlags::GUI_RECT_ALL_CORNERS} { +Rect::Rect(Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w, const uint32_t &h) + : borderColor(Color(0, 0)), fillColor(Color(15, 15)), penWidth{1}, penFocusWidth{1}, filled{false}, edges{RectangleEdgeFlags::GUI_RECT_ALL_EDGES}, + flatEdges{RectangleFlatFlags::GUI_RECT_FLAT_NO_FLAT}, corners{RectangleCornerFlags::GUI_RECT_ALL_CORNERS} +{ - widgetArea.x = 0; - widgetArea.y = 0; - widgetArea.w = 0; - widgetArea.h = 0; - this->parent = parent; - parent->addWidget(this); - - setPosition(x, y); - setSize(w, h); + widgetArea.x = 0; + widgetArea.y = 0; + widgetArea.w = 0; + widgetArea.h = 0; + this->parent = parent; + if (parent) + { + parent->addWidget(this); + } + setPosition(x, y); + setSize(w, h); } Rect::~Rect() {