~aleteoryx/muditaos

2f7224406226e684fb3654683f3b50f6375d359a — Przemyslaw Brudny 5 years ago f74051e
[EGD-3182] Implemented on CRUD notification page and on page item focus memory when rebuilding List. Applied for Phonebook and Messages.
M changelog.md => changelog.md +1 -0
@@ 11,6 11,7 @@

* `[desktopApp]` Fixed endpoint handling.
* `[phonebook]` Sort list of contacts by first name if there is no second name.
* `[listview]` List returning to previously focused element on CRUD notifications (Messages and Phonebook).

### Other
* `[desktopApp]` Added functional tests.

M module-apps/application-calendar/windows/AllEventsWindow.cpp => module-apps/application-calendar/windows/AllEventsWindow.cpp +1 -2
@@ 50,8 50,7 @@ namespace gui

    void AllEventsWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        allEventsList->clear();
        allEventsList->setProvider(allEventsModel);
        allEventsList->rebuildList();
    }

    bool AllEventsWindow::onInput(const gui::InputEvent &inputEvent)

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

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

    void MessagesMainWindow::buildInterface()

M module-apps/application-phonebook/models/NewContactModel.cpp => module-apps/application-phonebook/models/NewContactModel.cpp +1 -1
@@ 104,7 104,7 @@ void NewContactModel::clearData()

    createData();

    requestRecords(0, internalData.size());
    list->rebuildList();
}

void NewContactModel::saveData(std::shared_ptr<ContactRecord> contactRecord)

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

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

    void PhonebookMainWindow::buildInterface()

M module-gui/gui/widgets/BoxLayout.cpp => module-gui/gui/widgets/BoxLayout.cpp +23 -2
@@ 454,15 454,18 @@ namespace gui
        return granted;
    }

    void BoxLayout::setFocusOnElement(unsigned int elementNumber)
    bool BoxLayout::setFocusOnElement(unsigned int elementNumber)
    {
        unsigned int i = 0;
        bool success   = false;

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

                if (elementNumber == i) {
                    child->setFocus(true);
                    focusItem = child;
                    success   = true;
                }
                else {
                    child->setFocus(false);


@@ 470,6 473,8 @@ namespace gui
                ++i;
            }
        }

        return success;
    }

    void BoxLayout::setFocusOnLastElement()


@@ 488,6 493,22 @@ namespace gui
        }
    }

    unsigned int BoxLayout::getFocusItemIndex() const
    {
        auto index     = 0;
        auto focusItem = getFocusItem();

        for (auto child : children) {
            if (child == focusItem) {
                break;
            }
            if (child->activeItem && child->visible) {
                index++;
            }
        }
        return index;
    }

    Item *BoxLayout::getLastVisibleElement()
    {
        for (auto child = children.rbegin(); child != children.rend(); child++) {

M module-gui/gui/widgets/BoxLayout.hpp => module-gui/gui/widgets/BoxLayout.hpp +2 -1
@@ 101,6 101,7 @@ namespace gui
        /// set navigation from last to fist element in box
        virtual void setNavigation();
        std::list<Item *>::iterator getNavigationFocusedItem();
        unsigned int getFocusItemIndex() const;
        void setVisible(bool value) override;
        /// set visible but from previous scope... (page, element etc)
        void setVisible(bool value, bool previous);


@@ 109,7 110,7 @@ namespace gui
        /// if we want to do sth special (i.e. request new items)
        std::function<bool(const InputEvent &inputEvent)> borderCallback = nullptr;
        // set focus on specified box element
        void setFocusOnElement(unsigned int elementNumber);
        bool setFocusOnElement(unsigned int elementNumber);
        void setFocusOnLastElement();
        template <Axis axis>
        auto handleRequestResize(const Item *, unsigned short request_w, unsigned short request_h) -> Size;

M module-gui/gui/widgets/Item.cpp => module-gui/gui/widgets/Item.cpp +1 -1
@@ 467,7 467,7 @@ namespace gui
        checknrun(true);
    }

    Item *Item::getFocusItem()
    Item *Item::getFocusItem() const
    {
        if (focusItem) {
            auto subFocusItem = focusItem->getFocusItem();

M module-gui/gui/widgets/Item.hpp => module-gui/gui/widgets/Item.hpp +1 -1
@@ 144,7 144,7 @@ namespace gui
        void setFocusItem(Item *item);
        /// gettter for focus item
        /// @attention focusItem is just a pointer, might crash if item with focus was removed
        Item *getFocusItem();
        Item *getFocusItem() const;
        /// @}

        /// @defgroup callbackCallers   functions which should call functors from callbacks group

M module-gui/gui/widgets/ListView.cpp => module-gui/gui/widgets/ListView.cpp +76 -42
@@ 71,12 71,10 @@ namespace gui
                return false;
            }
            if (inputEvent.keyCode == KeyCode::KEY_UP && pageLoaded) {
                direction = style::listview::Direction::Top;
                return this->listPageEndReached();
                return this->requestPreviousPage();
            }
            else if (inputEvent.keyCode == KeyCode::KEY_DOWN && pageLoaded) {
                direction = style::listview::Direction::Bottom;
                return this->listPageEndReached();
                return this->requestNextPage();
            }
            else {
                return false;


@@ 124,13 122,40 @@ namespace gui
        }
    }

    void ListView::rebuildList()
    void ListView::rebuildList(style::listview::RebuildType rebuildType)
    {
        clear();
        setup(rebuildType);
        clearItems();

        setElementsCount(provider->requestRecordsCount());
        provider->requestRecords(0, calculateLimit());

        // If deletion operation caused last page to be removed request previous one.
        if (startIndex != 0 && startIndex == elementsCount) {
            requestPreviousPage();
        }
        else {
            provider->requestRecords(startIndex, calculateLimit());
        }
    };

    void ListView::setup(style::listview::RebuildType rebuildType)
    {
        if (rebuildType == style::listview::RebuildType::Full) {
            startIndex       = 0;
            storedFocusIndex = 0;
        }
        else {
            storedFocusIndex = body->getFocusItemIndex();

            if (direction == style::listview::Direction::Top) {
                storedFocusIndex = abs((currentPageSize - 1) - storedFocusIndex);
            }
        }

        body->setReverseOrder(false);
        direction = style::listview::Direction::Bottom;
    }

    std::shared_ptr<ListItemProvider> ListView::getProvider()
    {
        return provider;


@@ 258,6 283,15 @@ namespace gui
    {
        setFocusItem(body);

        if (storedFocusIndex != 0) {

            if (!body->setFocusOnElement(storedFocusIndex)) {
                body->setFocusOnLastElement();
            }

            storedFocusIndex = 0;
        }

        if (focusOnLastItem) {
            body->setFocusOnLastElement();
            focusOnLastItem = false;


@@ 287,7 321,7 @@ namespace gui
        return body->onInput(inputEvent);
    }

    int ListView::calculateMaxItemsOnPage()
    unsigned int ListView::calculateMaxItemsOnPage()
    {
        assert(provider->getMinimalItemHeight() != 0);
        auto count = widgetArea.h / provider->getMinimalItemHeight();


@@ 295,7 329,7 @@ namespace gui
        return count;
    }

    int ListView::calculateLimit()
    unsigned int ListView::calculateLimit()
    {
        auto minLimit =
            (2 * currentPageSize > calculateMaxItemsOnPage() ? 2 * currentPageSize : calculateMaxItemsOnPage());


@@ 305,54 339,54 @@ namespace gui
            return minLimit < startIndex ? minLimit : startIndex;
    }

    bool ListView::listPageEndReached()
    bool ListView::requestNextPage()
    {
        if (direction == style::listview::Direction::Bottom) {

            body->setReverseOrder(false);

            if (startIndex + currentPageSize >= elementsCount && listType == style::listview::Type::Continuous) {
        direction = style::listview::Direction::Bottom;
        body->setReverseOrder(false);

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

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

                startIndex = startIndex <= elementsCount - currentPageSize
                                 ? startIndex + currentPageSize
                                 : elementsCount - (elementsCount - startIndex);
            }
            return true;
        }
        else {

            pageLoaded = false;
            provider->requestRecords(startIndex, calculateLimit());
            startIndex = startIndex <= elementsCount - currentPageSize ? startIndex + currentPageSize
                                                                       : elementsCount - (elementsCount - startIndex);
        }

        if (direction == style::listview::Direction::Top) {
        pageLoaded = false;
        provider->requestRecords(startIndex, calculateLimit());

            body->setReverseOrder(true);
            auto topFetchIndex = 0;
        return true;
    }

            if (startIndex == 0 && listType == style::listview::Type::Continuous) {
    bool ListView::requestPreviousPage()
    {
        direction = style::listview::Direction::Top;
        body->setReverseOrder(true);
        auto topFetchIndex = 0;

                topFetchIndex = elementsCount - (elementsCount % currentPageSize);
                startIndex    = elementsCount;
            }
            else if (startIndex == 0 && listType == style::listview::Type::TopDown) {
        if (startIndex == 0 && listType == style::listview::Type::Continuous) {

                return true;
            }
            else {
            topFetchIndex = elementsCount - (elementsCount % currentPageSize);
            startIndex    = elementsCount;
        }
        else if (startIndex == 0 && listType == style::listview::Type::TopDown) {

                topFetchIndex = startIndex - calculateLimit() > 0 ? startIndex - calculateLimit() : 0;
            }
            return true;
        }
        else {

            pageLoaded = false;
            provider->requestRecords(topFetchIndex, calculateLimit());
            topFetchIndex = startIndex - calculateLimit() > 0 ? startIndex - calculateLimit() : 0;
        }

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

        return true;
    }


M module-gui/gui/widgets/ListView.hpp => module-gui/gui/widgets/ListView.hpp +14 -11
@@ 23,16 23,17 @@ namespace gui
    class ListView : public Rect
    {
      protected:
        int startIndex                             = 0;
        int elementsCount                          = 1;
        unsigned int startIndex                    = 0;
        unsigned int storedFocusIndex              = 0;
        unsigned int elementsCount                 = 1;
        std::shared_ptr<ListItemProvider> provider = nullptr;
        VBox *body                                 = nullptr;
        ListViewScroll *scroll                     = nullptr;

        int currentPageSize  = 0;
        bool pageLoaded      = false;
        bool focusOnLastItem = false;
        int scrollTopMargin  = style::margins::big;
        unsigned int currentPageSize = 0;
        bool pageLoaded              = false;
        bool focusOnLastItem         = false;
        int scrollTopMargin          = style::margins::big;

        style::listview::Type listType       = style::listview::Type::TopDown;
        style::listview::Direction direction = style::listview::Direction::Bottom;


@@ 44,10 45,12 @@ namespace gui
        void resizeWithScroll();
        void recalculateStartIndex();
        void checkFirstPage();
        int calculateMaxItemsOnPage();
        int calculateLimit();
        unsigned int calculateMaxItemsOnPage();
        unsigned int calculateLimit();
        Order getOrderFromDirection();
        virtual bool listPageEndReached();
        virtual bool requestNextPage();
        virtual bool requestPreviousPage();
        void setup(style::listview::RebuildType rebuildType);

      public:
        ListView();


@@ 56,13 59,13 @@ namespace gui

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

        // virtual methods from Item
        std::list<DrawCommand *> buildDrawList() override;

M module-gui/gui/widgets/Style.hpp => module-gui/gui/widgets/Style.hpp +6 -0
@@ 184,6 184,12 @@ namespace style
            Bottom
        };

        enum class RebuildType
        {
            Full,
            Partial
        };

        namespace scroll
        {
            const inline uint32_t x           = 0;

M module-gui/test/test-google/test-gui-listview.cpp => module-gui/test/test-google/test-gui-listview.cpp +8 -2
@@ 19,10 19,16 @@ class TestListView : public gui::ListView

    bool listBorderReached = false;

    bool listPageEndReached() override
    bool requestNextPage() override
    {
        listBorderReached = true;
        return ListView::listPageEndReached();
        return ListView::requestNextPage();
    }

    bool requestPreviousPage() override
    {
        listBorderReached = true;
        return ListView::requestPreviousPage();
    }

    TestListView(