~aleteoryx/muditaos

8030b273dfd317ac4d7f61c7af0ba0b18686c44d — Tomasz Langowski 5 years ago 652fc46
[EGD-5175] Fix grid layout navigation

Fix grid layout navigation in order to recognize
"not active" items
M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +6 -6
@@ 100,9 100,9 @@ namespace gui
            }
            case KeyCode::KEY_LEFT: {
                LOG_DEBUG("Call borderCallback -> go to the previous element");
                auto it = monthBox->getNavigationFocusedItem();
                if (monthBox->nextNavigationItem(std::prev(it)) != nullptr) {
                    monthBox->setFocusItem(monthBox->nextNavigationItem(std::prev(it)));
                auto it = std::prev(monthBox->getNavigationFocusedItem());
                if (it != monthBox->children.end() && (*it)->isActive()) {
                    monthBox->setFocusItem(*it);
                }
                else {
                    monthBox->setFocusOnLastElement();


@@ 111,9 111,9 @@ namespace gui
            }
            case KeyCode::KEY_RIGHT: {
                LOG_DEBUG("Call borderCallback -> go to the next element");
                auto it = monthBox->getNavigationFocusedItem();
                if (monthBox->nextNavigationItem(std::next(it)) != nullptr) {
                    monthBox->setFocusItem(monthBox->nextNavigationItem(std::next(it)));
                auto it = std::next(monthBox->getNavigationFocusedItem());
                if (it != monthBox->children.end() && (*it)->isActive()) {
                    monthBox->setFocusItem(*it);
                }
                else {
                    monthBox->setFocusOnElement(0);

M module-gui/gui/widgets/BoxLayout.cpp => module-gui/gui/widgets/BoxLayout.cpp +6 -6
@@ 110,7 110,7 @@ namespace gui
                /// this if back / front is crappy :|
                if (previous) {
                    for (auto el = children.rbegin(); el != children.rend(); ++el) {
                        if ((*el)->visible && (*el)->activeItem) {
                        if ((*el)->isActive()) {
                            setFocusItem(*el);
                            break;
                        }


@@ 118,7 118,7 @@ namespace gui
                }
                else {
                    for (auto &el : children) {
                        if (el->visible && el->activeItem) {
                        if (el->isActive()) {
                            setFocusItem(el);
                            break;
                        }


@@ 375,7 375,7 @@ namespace gui
    std::list<Item *>::iterator BoxLayout::nextNavigationItem(std::list<Item *>::iterator from)
    {
        return std::find_if(from, this->children.end(), [](auto &el) -> bool {
            if (el->visible && el->activeItem) {
            if (el->isActive()) {
                return true;
            }
            return false;


@@ 474,7 474,7 @@ namespace gui
        bool success   = false;

        for (auto child : children) {
            if (child->activeItem && child->visible) {
            if (child->isActive()) {

                if (elementNumber == i) {
                    child->setFocus(true);


@@ 496,7 496,7 @@ namespace gui
        auto last = true;
        for (auto child = children.rbegin(); child != children.rend(); child++) {

            if ((*child)->activeItem && (*child)->visible && last) {
            if ((*child)->isActive() && last) {
                (*child)->setFocus(true);
                focusItem = (*child);
                last      = false;


@@ 516,7 516,7 @@ namespace gui
            if (child == focusItem) {
                break;
            }
            if (child->activeItem && child->visible) {
            if (child->isActive()) {
                index++;
            }
        }

M module-gui/gui/widgets/GridLayout.cpp => module-gui/gui/widgets/GridLayout.cpp +44 -47
@@ 22,25 22,34 @@ GridLayout::GridLayout(

        auto it       = this->getNavigationFocusedItem();
        auto distance = std::distance(children.begin(), it);

        switch (inputEvent.keyCode) {
        case KeyCode::KEY_UP: {
            auto realRowSize = calculateRowSizeForBorderTransition(distance);
            this->setFocusItem((*std::next(it, (realRowSize - 1) * this->colSize)));
            auto col   = static_cast<uint32_t>(distance % colSize);
            Item *item = getFirstActiveItem(getLastColumnIndex(col), (-1) * static_cast<int>(colSize));
            if (item)
                this->setFocusItem(item);
            return true;
        }
        case KeyCode::KEY_DOWN: {
            auto realRowSize = calculateRowSizeForBorderTransition(distance);
            this->setFocusItem((*std::prev(it, (realRowSize - 1) * this->colSize)));
            auto col   = static_cast<uint32_t>(distance % colSize);
            Item *item = getFirstActiveItem(col, static_cast<int>(colSize));
            if (item)
                this->setFocusItem(item);
            return true;
        }
        case KeyCode::KEY_LEFT: {
            auto realColSize = calculateColumnSizeForBorderTransition(distance);
            this->setFocusItem((*std::next(it, realColSize - 1)));
            auto row   = static_cast<uint32_t>(distance / colSize);
            Item *item = getFirstActiveItem(getLastRowIndex(row), -1);
            if (item)
                this->setFocusItem(item);
            return true;
        }
        case KeyCode::KEY_RIGHT: {
            auto realColSize = calculateColumnSizeForBorderTransition(distance);
            this->setFocusItem((*std::prev(it, realColSize - 1)));
            auto row   = static_cast<uint32_t>(distance / colSize);
            Item *item = getFirstActiveItem(colSize * row, 1);
            if (item)
                this->setFocusItem(item);
            return true;
        }
        default: {


@@ 50,25 59,6 @@ GridLayout::GridLayout(
    };
}

uint32_t GridLayout::calculateColumnSizeForBorderTransition(const uint32_t currentPosition)
{
    auto realColSize = colSize;
    if (elementsInIncompletedLastRow) {
        if (((currentPosition / colSize) + 1) >= rowSize)
            realColSize = elementsInIncompletedLastRow;
    }
    return realColSize;
}
uint32_t GridLayout::calculateRowSizeForBorderTransition(const uint32_t currentPosition)
{
    auto realRowSize = rowSize;
    if (elementsInIncompletedLastRow) {
        if (((currentPosition % (colSize)) + 1) > elementsInIncompletedLastRow)
            realRowSize--;
    }
    return realRowSize;
}

void GridLayout::handleItemsOutOfGridLayoutArea(uint32_t maxItemsInArea)
{
    for (auto i = maxItemsInArea; i < children.size(); i++) {


@@ 88,16 78,6 @@ void GridLayout::resizeItems()
    uint32_t el_in_x = area().w / grid.x;
    uint32_t el_in_y = area().h / grid.y;

    elementsInIncompletedLastRow = 0;
    colSize = children.size() < area().w / grid.x ? children.size() : area().w / grid.x;
    rowSize = colSize != 0 ? (children.size() / colSize) : 1;
    if (colSize > 1 && (static_cast<double>(children.size()) / colSize) > 1.0) {
        elementsInIncompletedLastRow = children.size() % colSize;
    }
    if (elementsInIncompletedLastRow > 0) {
        rowSize++;
    }

    uint32_t strech_x     = 0;
    uint32_t strech_y     = 0;
    uint32_t max_elements = el_in_x * el_in_y;


@@ 113,6 93,13 @@ void GridLayout::resizeItems()
        handleItemsOutOfGridLayoutArea(max_elements);
        return;
    }

    colSize = children.size() < area().w / grid.x ? children.size() : area().w / grid.x;
    rowSize = colSize != 0 ? (children.size() / colSize) : 1;
    if (colSize > 1 && (static_cast<double>(children.size()) / colSize) > 1.0 && (children.size() % colSize)) {
        rowSize++;
    }

    if (el_in_x > 2)
        strech_x = (area().w - grid.x * el_in_x) / (el_in_x - 1);
    if (el_in_y > 2)


@@ 149,28 136,38 @@ void GridLayout::setNavigation()
    for (auto it = children.begin(); it != children.end(); ++it, ++i) {

        if (it != children.begin() && (i + 1) % colSize != 1) {
            (*it)->setNavigationItem(NavigationDirection::LEFT, nextNavigationItem(std::prev(it)));
            (*it)->setNavigationItem(NavigationDirection::LEFT, getFirstActiveItem(i - 1, -1));
        }

        if (it != std::prev(children.end()) && (i + 1) % colSize != 0) {
            (*it)->setNavigationItem(NavigationDirection::RIGHT, nextNavigationItem(std::next(it)));
            (*it)->setNavigationItem(NavigationDirection::RIGHT, getFirstActiveItem(i + 1, 1));
        }

        if ((i - offset) >= 0) {
            (*it)->setNavigationItem(NavigationDirection::UP, nextNavigationItem(std::prev(it, offset)));
            (*it)->setNavigationItem(NavigationDirection::UP, getFirstActiveItem(i - offset, (-1) * offset));
        }
        if ((i + offset) < static_cast<int>(children.size())) {
            (*it)->setNavigationItem(NavigationDirection::DOWN, nextNavigationItem(std::next(it, offset)));
            (*it)->setNavigationItem(NavigationDirection::DOWN, getFirstActiveItem(i + offset, offset));
        }
    }
}

Item *GridLayout::nextNavigationItem(std::list<Item *>::iterator it)
Item *GridLayout::getFirstActiveItem(uint32_t startposition, int step)
{
    if (it != this->children.end() && (*it)->visible && (*it)->activeItem) {
        return *it;
    }
    else {
        return nullptr;
    Item *retItem = nullptr;
    int index     = static_cast<int>(startposition);
    uint32_t row  = startposition / colSize;
    while (index >= 0 && index < static_cast<int>(children.size())) {
        ///> condition for movement along row (+1,-1 step)
        if ((step == 1 || step == -1) && (index / colSize != row)) {
            break;
        }
        std::list<Item *>::iterator tmpit = std::next(children.begin(), index);
        if ((*tmpit)->isActive()) {
            retItem = *tmpit;
            break;
        }
        index += step;
    }
    return retItem;
}

M module-gui/gui/widgets/GridLayout.hpp => module-gui/gui/widgets/GridLayout.hpp +15 -9
@@ 26,23 26,29 @@ namespace gui
        {}
        GridLayout() : GridLayout(0, 0, 0, 0, {0, 0})
        {}
        /// when reached top -> start from bottom. When reached left, start from right.
        bool navigationRotate = true;
        void resizeItems() override;
        void setNavigation() override;
        Item *nextNavigationItem(std::list<Item *>::iterator it);

        uint32_t rowSize = 0;
        uint32_t colSize = 0;
        ///> elementsInIncompletedLastRow describes how many items has been put to last row,
        /// in case when items for last row is not equal to colSize
        uint32_t elementsInIncompletedLastRow = 0;

      private:
        uint32_t calculateColumnSizeForBorderTransition(const uint32_t currentPosition);
        uint32_t calculateRowSizeForBorderTransition(const uint32_t currentPosition);

        void handleItemsOutOfGridLayoutArea(uint32_t maxItemsInArea);
        Item *getFirstActiveItem(uint32_t startposition, int step);
        inline uint32_t getLastColumnIndex(uint32_t col)
        {
            auto lastcolumnindex = col;
            while ((lastcolumnindex + colSize) < children.size())
                lastcolumnindex += colSize;
            return lastcolumnindex;
        }
        inline uint32_t getLastRowIndex(uint32_t row)
        {
            uint32_t lastrowindex = colSize * row + (colSize - 1);
            while (lastrowindex >= children.size())
                lastrowindex--;
            return lastrowindex;
        }
    };

}; // namespace gui

M module-gui/gui/widgets/Item.hpp => module-gui/gui/widgets/Item.hpp +5 -0
@@ 344,6 344,11 @@ namespace gui
        /// remove timer from item and as a result - destory it
        void detachTimer(Timer &timer);

        /// simple check function to determine if item is active && visible
        inline bool isActive()
        {
            return (activeItem && visible);
        }
        virtual void accept(GuiVisitor &visitor);

      protected:

M module-gui/test/test-google/test-gui-gridlayout.cpp => module-gui/test/test-google/test-gui-gridlayout.cpp +150 -2
@@ 67,7 67,20 @@ class GridLayoutTesting : public ::testing::Test
            Box->addWidget(item);
        }
    }

    void addItem(gui::BoxLayout *Box,
                 uint32_t item_w,
                 uint32_t item_h,
                 uint32_t id,
                 bool active                 = true,
                 const gui::Margins &margins = gui::Margins())
    {
        auto item     = new TestItem(nullptr, 0, 0, item_w, item_h);
        item->ID      = id;
        item->visible = true;
        item->setMargins(margins);
        item->activeItem = active;
        Box->addWidget(item);
    }
    gui::GridLayout *gridLayout = nullptr;

    ///> GridLayout test constants


@@ 120,6 133,139 @@ TEST_F(GridLayoutTesting, Navigate_Test)
    ASSERT_EQ(1 + expColSize, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 + expColSize << " should have focus";
}

TEST_F(GridLayoutTesting, Navigate_Test_ActiveItems_1)
{
    ///> Test for grid layout with 48 elements (A - active item, N - non active item, NV - non visible item)
    ///> | 1   A | 2  NA | 3   A | 4  NA | 5   A | 6  NA | 7  A  | 8  NA | 9   A | 10 NA | 11 A  | 12 NA |
    ///> | 13 NA | 14  A | 15 NA | 16  A | 17 NA | 18  A | 19 NA | 20  A | 21 NA | 22  A | 23 NA | 24  A |
    ///> | 25  A | 26 NA | 27  A | 28 NA | 29  A | 30 NA | 31 A  | 32 NA | 33  A | 34 NA | 35 A  | 36 NA |
    ///> | 37 NA | 38  A | 39 NA | 40  A | 41 NA | 42  A | 43 NA | 44  A | 45 NA | 46  A | 47 NA | 48  A |
    ///> | 49 NV | 50 NV | 51 NV | 52 NV |
    for (uint32_t i = 1; i <= 12; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (uint32_t i = 13; i <= 24; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    for (uint32_t i = 25; i <= 36; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (uint32_t i = 37; i <= 48; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    ///> Add some items to exceed grid layout area
    for (uint32_t i = 49; i <= 52; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, true);
    }
    gridLayout->setFocus(true);
    ASSERT_EQ(gridLayout->rowSize, 4) << "row size is not " << 4 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 12) << "col size is not " << 12 << " as expected";
    ASSERT_EQ(52, gridLayout->children.size()) << "GridLayout should contain " << 52 << " elements";

    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 << " should have focus";
    moveNTimes(gridLayout, 2, gui::KeyCode::KEY_RIGHT);
    ASSERT_EQ(5, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 5 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(5 + (2 * expColSize), dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 5 + (2 * expColSize) << " should have focus";
    moveNTimes(gridLayout, 2, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(1 + (2 * expColSize), dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 + (2 * expColSize) << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 << " should have focus";
}

TEST_F(GridLayoutTesting, Navigate_Test_ActiveItems_2_BorderCallback)
{
    ///> Test for grid layout with 48 elements
    ///> | 1   A | 2  NA | 3   A | 4  NA | 5   A | 6  NA | 7  A  | 8  NA | 9   A | 10 NA | 11 A  | 12 NA |
    ///> | 13 NA | 14  A | 15 NA | 16  A | 17 NA | 18  A | 19 NA | 20  A | 21 NA | 22  A | 23 NA | 24  A |
    ///> | 25  A | 26 NA | 27  A | 28 NA | 29  A | 30 NA | 31 A  | 32 NA | 33  A | 34 NA | 35 A  | 36 NA |
    ///> | 37 NA | 38  A | 39 NA |
    for (uint32_t i = 1; i <= 12; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (uint32_t i = 13; i <= 24; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }
    for (uint32_t i = 25; i <= 36; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, (i % 2) ? true : false);
    }
    for (uint32_t i = 37; i <= 39; i++) {
        addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, i, ((i + 1) % 2) ? true : false);
    }

    gridLayout->setFocus(true);
    ASSERT_EQ(gridLayout->rowSize, 4) << "row size is not " << 4 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 12) << "col size is not " << 12 << " as expected";
    ASSERT_EQ(39, gridLayout->children.size()) << "GridLayout should contain " << 39 << " elements";

    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 << " should have focus";
    moveNTimes(gridLayout, 2, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(9, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 9 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_UP);
    ASSERT_EQ(33, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 33 << " should have focus";
    moveNTimes(gridLayout, 3, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(27, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 27 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(3, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 3 << " should have focus";
    moveNTimes(gridLayout, 5, gui::KeyCode::KEY_RIGHT);
    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 1 << " should have focus";

    ///> Test for grid layout with 1 element
    ///> | 1NA |
    gridLayout->erase();
    addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, 1, false);
    ASSERT_EQ(gridLayout->children.size(), 1) << "elements size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->rowSize, 1) << "row size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 1) << "col size is not " << 1 << " as expected";
    gridLayout->setFocus(false);
    gridLayout->setFocus(true);
    ASSERT_EQ(nullptr, gridLayout->getFocusItem()) << "no element shall be focused";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(nullptr, gridLayout->getFocusItem()) << "no element shall be focused";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_RIGHT);
    ASSERT_EQ(nullptr, gridLayout->getFocusItem()) << "no element shall be focused";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_UP);
    ASSERT_EQ(nullptr, gridLayout->getFocusItem()) << "no element shall be focused";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(nullptr, gridLayout->getFocusItem()) << "no element shall be focused";
    ///> Test for grid layout with 1 element
    ///> | 1NA | 1A |
    gridLayout->erase();
    addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, 1, false);
    addItem(gridLayout, testStyle::grid_item_w, testStyle::grid_item_h, 2, true);
    ASSERT_EQ(gridLayout->children.size(), 2) << "elements size is not " << 2 << " as expected";
    ASSERT_EQ(gridLayout->rowSize, 1) << "row size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 2) << "col size is not " << 2 << " as expected";
    gridLayout->setFocus(false);
    gridLayout->setFocus(true);
    ASSERT_EQ(2, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 2 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(2, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 2 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_RIGHT);
    ASSERT_EQ(2, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 2 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_UP);
    ASSERT_EQ(2, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 2 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(2, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 2 << " should have focus";
}

///> TODO: Enable this test when issue with setFocus will be resolved
TEST_F(GridLayoutTesting, DISABLED_Border_Callback_Test)
{


@@ 149,7 295,7 @@ TEST_F(GridLayoutTesting, DISABLED_Border_Callback_Test)
        << "element with ID " << 37 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_LEFT);
    ASSERT_EQ(46, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 37 << " should have focus";
        << "element with ID " << 46 << " should have focus";
    moveNTimes(gridLayout, 1, gui::KeyCode::KEY_DOWN);
    ASSERT_EQ(10, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)
        << "element with ID " << 10 << " should have focus";


@@ 170,6 316,7 @@ TEST_F(GridLayoutTesting, DISABLED_Border_Callback_Test)
    ASSERT_EQ(gridLayout->children.size(), 1) << "elements size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->rowSize, 1) << "row size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 1) << "col size is not " << 1 << " as expected";

    gridLayout->setFocus(true);

    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)


@@ 194,6 341,7 @@ TEST_F(GridLayoutTesting, DISABLED_Border_Callback_Test)
    ASSERT_EQ(gridLayout->children.size(), 2) << "elements size is not " << 2 << " as expected";
    ASSERT_EQ(gridLayout->rowSize, 1) << "row size is not " << 1 << " as expected";
    ASSERT_EQ(gridLayout->colSize, 2) << "col size is not " << 2 << " as expected";

    gridLayout->setFocus(true);

    ASSERT_EQ(1, dynamic_cast<TestItem *>(gridLayout->getFocusItem())->ID)