~aleteoryx/muditaos

f4ab64d99686a8fd2e2e0568fde4cb0f64fca1a5 — Przemyslaw Brudny 5 years ago 2e80e11
[EGD-3760] ListView added support to rebuild on provided offset.
M changelog.md => changelog.md +1 -0
@@ 18,6 18,7 @@
### Other

* `[system_features]` Sprintf clean up.
* `[listview]` Added ability to rebuild on provided data offset. 

## [0.38.2 2020-09-18]


M module-apps/application-messages/windows/MessagesMainWindow.cpp => module-apps/application-messages/windows/MessagesMainWindow.cpp +2 -2
@@ 28,7 28,7 @@ namespace gui

    void MessagesMainWindow::rebuild()
    {
        list->rebuildList(style::listview::RebuildType::Partial);
        list->rebuildList(style::listview::RebuildType::InPlace);
    }

    void MessagesMainWindow::buildInterface()


@@ 116,7 116,7 @@ namespace gui
        }
        else {
            emptyListIcon->setVisible(false);
            list->rebuildList(style::listview::RebuildType::Partial);
            list->rebuildList(style::listview::RebuildType::InPlace);
        }

        DBServiceAPI::GetQuery(application,

M module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp => module-apps/application-phonebook/widgets/InputLinesWithLabelIWidget.cpp +3 -1
@@ 72,7 72,9 @@ namespace gui

        inputCallback = [&](Item &item, const InputEvent &event) {
            auto result = inputText->onInput(event);
            checkTextContent();
            if (checkTextContent != nullptr) {
                checkTextContent();
            }
            return result;
        };
        setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES);

M module-apps/application-phonebook/windows/PhonebookMainWindow.cpp => module-apps/application-phonebook/windows/PhonebookMainWindow.cpp +1 -1
@@ 20,7 20,7 @@ namespace gui

    void PhonebookMainWindow::rebuild()
    {
        contactsList->rebuildList(style::listview::RebuildType::Partial);
        contactsList->rebuildList(style::listview::RebuildType::InPlace);
    }

    void PhonebookMainWindow::buildInterface()

M module-gui/gui/widgets/ListView.cpp => module-gui/gui/widgets/ListView.cpp +26 -14
@@ 97,14 97,14 @@ namespace gui
        clearItems();
    }

    void ListView::setElementsCount(int count)
    void ListView::setElementsCount(unsigned int count)
    {
        elementsCount = count;
    }

    void ListView::setListViewType(style::listview::Type type)
    void ListView::setBoundaries(style::listview::Boundaries value)
    {
        listType = type;
        boundaries = value;
    }

    void ListView::setScrollTopMargin(int value)


@@ 122,13 122,13 @@ namespace gui
        }
    }

    void ListView::rebuildList(style::listview::RebuildType rebuildType)
    void ListView::rebuildList(style::listview::RebuildType rebuildType, unsigned int dataOffset)
    {
        setup(rebuildType);
        clearItems();

        setElementsCount(provider->requestRecordsCount());

        setup(rebuildType, dataOffset);
        clearItems();

        // If deletion operation caused last page to be removed request previous one.
        if (startIndex != 0 && startIndex == elementsCount) {
            requestPreviousPage();


@@ 138,13 138,22 @@ namespace gui
        }
    };

    void ListView::setup(style::listview::RebuildType rebuildType)
    void ListView::setup(style::listview::RebuildType rebuildType, unsigned int dataOffset)
    {
        if (rebuildType == style::listview::RebuildType::Full) {
            startIndex       = 0;
            storedFocusIndex = 0;
        }
        else {
        else if (rebuildType == style::listview::RebuildType::OnOffset) {
            if (dataOffset < elementsCount) {
                startIndex       = dataOffset;
                storedFocusIndex = 0;
            }
            else {
                LOG_ERROR("Requested rebuild on index greater than elements count");
            }
        }
        else if (rebuildType == style::listview::RebuildType::InPlace) {
            storedFocusIndex = body->getFocusItemIndex();

            if (direction == style::listview::Direction::Top) {


@@ 344,11 353,11 @@ namespace gui
        direction = style::listview::Direction::Bottom;
        body->setReverseOrder(false);

        if (startIndex + currentPageSize >= elementsCount && listType == style::listview::Type::Continuous) {
        if (startIndex + currentPageSize >= elementsCount && boundaries == style::listview::Boundaries::Continuous) {

            startIndex = 0;
        }
        else if (startIndex + currentPageSize >= elementsCount && listType == style::listview::Type::TopDown) {
        else if (startIndex + currentPageSize >= elementsCount && boundaries == style::listview::Boundaries::Fixed) {

            return true;
        }


@@ 369,23 378,26 @@ namespace gui
        direction = style::listview::Direction::Top;
        body->setReverseOrder(true);
        auto topFetchIndex = 0;
        auto limit         = 0;

        if (startIndex == 0 && listType == style::listview::Type::Continuous) {
        if (startIndex == 0 && boundaries == style::listview::Boundaries::Continuous) {

            topFetchIndex = elementsCount - (elementsCount % currentPageSize);
            startIndex    = elementsCount;
            limit         = calculateLimit() - topFetchIndex;
        }
        else if (startIndex == 0 && listType == style::listview::Type::TopDown) {
        else if (startIndex == 0 && boundaries == style::listview::Boundaries::Fixed) {

            return true;
        }
        else {

            limit         = calculateLimit();
            topFetchIndex = startIndex - calculateLimit() > 0 ? startIndex - calculateLimit() : 0;
        }

        pageLoaded = false;
        provider->requestRecords(topFetchIndex, calculateLimit());
        provider->requestRecords(topFetchIndex, limit);

        return true;
    }

M module-gui/gui/widgets/ListView.hpp => module-gui/gui/widgets/ListView.hpp +7 -6
@@ 35,8 35,8 @@ namespace gui
        bool focusOnLastItem         = false;
        int scrollTopMargin          = style::margins::big;

        style::listview::Type listType       = style::listview::Type::TopDown;
        style::listview::Direction direction = style::listview::Direction::Bottom;
        style::listview::Boundaries boundaries = style::listview::Boundaries::Fixed;
        style::listview::Direction direction   = style::listview::Direction::Bottom;

        void clearItems();
        virtual void addItemsOnPage();


@@ 50,19 50,20 @@ namespace gui
        Order getOrderFromDirection();
        virtual bool requestNextPage();
        virtual bool requestPreviousPage();
        void setup(style::listview::RebuildType rebuildType);
        void setup(style::listview::RebuildType rebuildType, unsigned int dataOffset = 0);

      public:
        ListView();
        ListView(Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, std::shared_ptr<ListItemProvider> prov);
        ~ListView();

        void setElementsCount(int count);
        void setElementsCount(unsigned int count);
        void setProvider(std::shared_ptr<ListItemProvider> provider);
        void rebuildList(style::listview::RebuildType rebuildType = style::listview::RebuildType::Full);
        void rebuildList(style::listview::RebuildType rebuildType = style::listview::RebuildType::Full,
                         unsigned int dataOffset                  = 0);
        void clear();
        std::shared_ptr<ListItemProvider> getProvider();
        void setListViewType(style::listview::Type type);
        void setBoundaries(style::listview::Boundaries value);
        void setScrollTopMargin(int value);
        void setAlignment(const Alignment &value) override;
        void onProviderDataUpdate();

M module-gui/gui/widgets/Style.hpp => module-gui/gui/widgets/Style.hpp +12 -5
@@ 172,22 172,29 @@ namespace style

    namespace listview
    {
        enum class Type
        /// Possible List boundaries handling types
        enum class Boundaries
        {
            TopDown,
            Continuous
            Fixed,     ///< Fixed - list will stop scrolling on first or last elements on appropriate top or bottom
                       ///< directions.
            Continuous ///< Continuous - list will continue to beginning or end on first or last elements on
                       ///< appropriate top or bottom directions.
        };

        /// Possible List scrolling directions
        enum class Direction
        {
            Top,
            Bottom
        };

        /// Possible List rebuild types
        enum class RebuildType
        {
            Full,
            Partial
            Full,    ///< Full rebuild - resets lists to all initial conditions and request data from beginning.
            InPlace, ///< InPlace rebuild - stores currently focused part of list and rebuild from that part.
            OnOffset ///< OnOffset rebuild - resets lists to all initial conditions and request data from provided
                     ///< offset.
        };

        namespace scroll

M module-gui/test/mock/TestListViewProvider.cpp => module-gui/test/mock/TestListViewProvider.cpp +4 -1
@@ 55,7 55,7 @@ namespace gui
    {
        unsigned int index = 0;
        if (order == gui::Order::Previous) {
            index = internalLimit - modelIndex - 1;
            index = internalOffset + internalLimit - 1 - modelIndex;
        }
        if (order == gui::Order::Next) {
            index = internalOffset + modelIndex;


@@ 88,6 88,9 @@ namespace gui
                }

                internalData[index]->setVisible(true);
                internalData[index]->setFocus(false);
                internalData[index]->clearNavigationItem(gui::NavigationDirection::UP);
                internalData[index]->clearNavigationItem(gui::NavigationDirection::DOWN);
                internalData[index]->setMargins(testItemMargins);

                modelIndex++;

M module-gui/test/test-google/test-gui-listview.cpp => module-gui/test/test-google/test-gui-listview.cpp +57 -2
@@ 16,6 16,7 @@ class TestListView : public gui::ListView
    FRIEND_TEST(ListViewTesting, Navigate_Test);
    FRIEND_TEST(ListViewTesting, Continuous_Type_Test);
    FRIEND_TEST(ListViewTesting, Data_Deletion_Test);
    FRIEND_TEST(ListViewTesting, Rebuild_Type_Test);

    bool listBorderReached = false;



@@ 225,7 226,7 @@ TEST_F(ListViewTesting, Navigate_Test)
TEST_F(ListViewTesting, Continuous_Type_Test)
{
    // set list type to Continuous
    testListView->setListViewType(style::listview::Type::Continuous);
    testListView->setBoundaries(style::listview::Boundaries::Continuous);

    // 10 provider elements, 100 h each, list 600 -> 6 elements on page.
    testListView->provider->requestRecords(0, 10);


@@ 234,7 235,7 @@ TEST_F(ListViewTesting, Continuous_Type_Test)
    ASSERT_TRUE(testListView->listBorderReached) << "Navigate top by one - page should change to last page";
    testListView->listBorderReached = false;
    ASSERT_EQ(4, testListView->currentPageSize) << "4 elements should be displayed";
    //

    ASSERT_EQ(9, dynamic_cast<gui::TestListItem *>(testListView->body->children.front())->ID)
        << "First element ID should be 9";
    ASSERT_EQ(6, dynamic_cast<gui::TestListItem *>(testListView->body->children.back())->ID)


@@ 316,3 317,57 @@ TEST_F(ListViewTesting, Data_Deletion_Test)
    ASSERT_NE(pointerToLast, dynamic_cast<gui::TestListItem *>(testListView->body->children.back()))
        << "Pointer to last element should match";
}

TEST_F(ListViewTesting, Rebuild_Type_Test)
{
    // Internal data -> do not delete
    testProvider->dataSource = gui::TestListViewDataSource::Internal;

    // Do full list rebuild
    testListView->rebuildList(style::listview::RebuildType::Full);

    ASSERT_EQ(6, testListView->currentPageSize) << "6 elements should fit into list.";
    ASSERT_EQ(0, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 0 has focus";

    moveNTimes(3, style::listview::Direction::Bottom);
    ASSERT_FALSE(testListView->listBorderReached) << "Navigate bot by 3 - page should not change";
    ASSERT_EQ(3, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 3 has focus";

    auto pointerToFocusedElement = dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem());

    // Do in place rebuild
    testListView->rebuildList(style::listview::RebuildType::InPlace);

    // Check if focused item did not change
    ASSERT_EQ(3, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 3 has focus";
    ASSERT_EQ(pointerToFocusedElement, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem()))
        << "Focused item should be same";

    // Do on offset rebuild on 8 element
    testListView->rebuildList(style::listview::RebuildType::OnOffset, 8);

    // Page should change and 2 elements should fit (10 - 8)
    ASSERT_EQ(2, testListView->currentPageSize) << "2 elements should fit into list.";
    ASSERT_EQ(8, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 8 has focus";

    moveNTimes(2, style::listview::Direction::Top);
    ASSERT_TRUE(testListView->listBorderReached) << "Navigate Top by 3 - page should change";
    ASSERT_EQ(6, testListView->currentPageSize) << "6 elements should fit into list.";
    ASSERT_EQ(6, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 5 has focus";

    pointerToFocusedElement = dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem());

    // Do in place rebuild
    testListView->rebuildList(style::listview::RebuildType::InPlace);

    // Check if focused item did not change
    ASSERT_EQ(6, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem())->ID)
        << "Check if item 6 has focus";
    ASSERT_EQ(pointerToFocusedElement, dynamic_cast<gui::TestListItem *>(testListView->body->getFocusItem()))
        << "Focused item should be same";
}