From d46139195c0e10314122dce413ae9efea871eba0 Mon Sep 17 00:00:00 2001 From: PrzeBrudny <60609703+PrzeBrudny@users.noreply.github.com> Date: Mon, 14 Dec 2020 21:56:11 +0100 Subject: [PATCH] [EGD-3442] Fix list empty window unhandled behaviours, in place rebuild handling, notes rebuild on note deletion and changed call Icon name to stateIcon. (#1160) --- changelog.md | 2 + image/assets/lang/English.json | 1 + module-apps/application-call/CMakeLists.txt | 2 +- .../widgets/{Icon.hpp => StateIcon.hpp} | 14 +++--- .../widgets/{Icons.cpp => StateIcons.cpp} | 19 +++---- .../widgets/{Icons.hpp => StateIcons.hpp} | 10 ++-- .../application-call/windows/CallWindow.cpp | 2 +- .../application-call/windows/CallWindow.hpp | 2 +- .../windows/EnterNumberWindow.hpp | 4 +- .../models/SMSTemplateModel.cpp | 2 +- .../models/SMSThreadModel.cpp | 2 +- .../models/ThreadsModel.cpp | 2 +- .../models/ThreadsSearchResultsModel.cpp | 2 +- .../windows/MessagesMainWindow.cpp | 21 +++----- .../windows/SearchResults.cpp | 3 ++ .../application-notes/ApplicationNotes.cpp | 18 ++++++- .../model/NotesListModel.cpp | 2 +- .../windows/NoteMainWindow.cpp | 49 ++++++++----------- .../windows/NoteMainWindow.hpp | 3 -- module-gui/gui/widgets/ListView.cpp | 43 +++++++++++++--- module-gui/gui/widgets/ListView.hpp | 16 +++++- 21 files changed, 132 insertions(+), 87 deletions(-) rename module-apps/application-call/widgets/{Icon.hpp => StateIcon.hpp} (94%) rename module-apps/application-call/widgets/{Icons.cpp => StateIcons.cpp} (66%) rename module-apps/application-call/widgets/{Icons.hpp => StateIcons.hpp} (78%) diff --git a/changelog.md b/changelog.md index 8d3410266..079ef2bd8 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,8 @@ * `[PowerManagement]` Added CPU load measurement. * `[alarms]` Added new/edit alarm window * `[file indexer]` Support for deleting entries in File Indexer DB. +* `[listview]` Added onEmpty list callbacks and implemented them for notes and messages. +* `[notes]` Added list rebuild on notifications. ### Changed diff --git a/image/assets/lang/English.json b/image/assets/lang/English.json index 2393e59e7..65a30f277 100644 --- a/image/assets/lang/English.json +++ b/image/assets/lang/English.json @@ -58,6 +58,7 @@ "common_today": "Today", "common_results_prefix": "Results: ", "common_search": "SEARCH", + "common_empty_list": "Default Info: No elements on list.", "locale_12hour_min": "%I:%M %p", "locale_24hour_min": "%H:%M", "locale_date_full": "%m/%d/%y", diff --git a/module-apps/application-call/CMakeLists.txt b/module-apps/application-call/CMakeLists.txt index bb6037d80..a782c48de 100644 --- a/module-apps/application-call/CMakeLists.txt +++ b/module-apps/application-call/CMakeLists.txt @@ -21,7 +21,7 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/windows/EnterNumberWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/EmergencyCallWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/CallWindow.cpp" - "${CMAKE_CURRENT_LIST_DIR}/widgets/Icons.cpp" + "${CMAKE_CURRENT_LIST_DIR}/widgets/StateIcons.cpp" PUBLIC "${CMAKE_CURRENT_LIST_DIR}/ApplicationCall.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/EnterNumberWindow.hpp" diff --git a/module-apps/application-call/widgets/Icon.hpp b/module-apps/application-call/widgets/StateIcon.hpp similarity index 94% rename from module-apps/application-call/widgets/Icon.hpp rename to module-apps/application-call/widgets/StateIcon.hpp index c9118c871..85ee07df2 100644 --- a/module-apps/application-call/widgets/Icon.hpp +++ b/module-apps/application-call/widgets/StateIcon.hpp @@ -37,12 +37,14 @@ namespace gui /// @note Desired visible area is icon::w x icon::h. However, the whole widget area might be set to bigger one w x /// icon::h where w = icon::w + 2 * w_margin. It is necessary as it is possible that text will exceed Icon visible /// area - template class Icon : public Rect + + template class [[deprecated("Class to be merged with Icon.hpp")]] StateIcon : public Rect { public: using IconMap = std::map>; - Icon() = delete; - Icon(Item *parent, const uint32_t &x, const uint32_t &y, const uint32_t &w_margin, T state, const IconMap &data) + StateIcon() = delete; + StateIcon( + Item * parent, const uint32_t &x, const uint32_t &y, const uint32_t &w_margin, T state, const IconMap &data) : Rect(parent, x, y, icon::w + 2 * w_margin, icon::h), data(data) { setEdges(RectangleEdge::None); @@ -62,10 +64,8 @@ namespace gui set(state); } - virtual ~Icon() - { - - } + virtual ~StateIcon() + {} /// @brief sets Icon state and display appropriate image and string /// @param state to set. @note state is also used as a key to the internal map. If the state points to the diff --git a/module-apps/application-call/widgets/Icons.cpp b/module-apps/application-call/widgets/StateIcons.cpp similarity index 66% rename from module-apps/application-call/widgets/Icons.cpp rename to module-apps/application-call/widgets/StateIcons.cpp index a31e45062..956c64be0 100644 --- a/module-apps/application-call/widgets/Icons.cpp +++ b/module-apps/application-call/widgets/StateIcons.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md -#include "Icons.hpp" +#include "StateIcons.hpp" #include "application-call/data/CallAppStyle.hpp" @@ -22,30 +22,31 @@ namespace gui constexpr auto speakerStr = "app_call_speaker"; constexpr auto speakerOnStr = "app_call_speaker_on"; - const Icon::IconMap contactIconMap = { + const StateIcon::IconMap contactIconMap = { {AddContactIconState::ADD_CONTACT, {crossImg, addContactStr}}}; - const Icon::IconMap smsIconMap = {{SendSmsIconState::SEND_SMS, {messageImg, sendSmstStr}}}; - const Icon::IconMap microphoneIconMap = { + const StateIcon::IconMap smsIconMap = { + {SendSmsIconState::SEND_SMS, {messageImg, sendSmstStr}}}; + const StateIcon::IconMap microphoneIconMap = { {MicrophoneIconState::MUTE, {muteImg, muteStr}}, {MicrophoneIconState::MUTED, {mutedImg, mutedStr}}}; - const Icon::IconMap speakerIconMap = { + const StateIcon::IconMap speakerIconMap = { {SpeakerIconState::SPEAKER, {speakerImg, speakerStr}}, {SpeakerIconState::SPEAKERON, {speakerOnImg, speakerOnStr}}}; } // namespace AddContactIcon::AddContactIcon(Item *parent, std::uint32_t x, std::uint32_t y) - : Icon(parent, x, y, callAppStyle::icon::x_margin, AddContactIconState::ADD_CONTACT, contactIconMap) + : StateIcon(parent, x, y, callAppStyle::icon::x_margin, AddContactIconState::ADD_CONTACT, contactIconMap) {} SendSmsIcon::SendSmsIcon(Item *parent, std::uint32_t x, std::uint32_t y) - : Icon(parent, x, y, callAppStyle::icon::x_margin, SendSmsIconState::SEND_SMS, smsIconMap) + : StateIcon(parent, x, y, callAppStyle::icon::x_margin, SendSmsIconState::SEND_SMS, smsIconMap) {} MicrophoneIcon::MicrophoneIcon(Item *parent, std::uint32_t x, std::uint32_t y) - : Icon(parent, x, y, callAppStyle::icon::x_margin, MicrophoneIconState::MUTE, microphoneIconMap) + : StateIcon(parent, x, y, callAppStyle::icon::x_margin, MicrophoneIconState::MUTE, microphoneIconMap) {} SpeakerIcon::SpeakerIcon(Item *parent, std::uint32_t x, std::uint32_t y) - : Icon(parent, x, y, callAppStyle::icon::x_margin, SpeakerIconState::SPEAKER, speakerIconMap) + : StateIcon(parent, x, y, callAppStyle::icon::x_margin, SpeakerIconState::SPEAKER, speakerIconMap) {} } // namespace gui diff --git a/module-apps/application-call/widgets/Icons.hpp b/module-apps/application-call/widgets/StateIcons.hpp similarity index 78% rename from module-apps/application-call/widgets/Icons.hpp rename to module-apps/application-call/widgets/StateIcons.hpp index a3bc4d966..1b20f2937 100644 --- a/module-apps/application-call/widgets/Icons.hpp +++ b/module-apps/application-call/widgets/StateIcons.hpp @@ -3,7 +3,7 @@ #pragma once -#include "Icon.hpp" +#include "StateIcon.hpp" #include @@ -14,7 +14,7 @@ namespace gui ADD_CONTACT }; - class AddContactIcon : public Icon + class AddContactIcon : public StateIcon { public: AddContactIcon() = delete; @@ -25,7 +25,7 @@ namespace gui { SEND_SMS }; - class SendSmsIcon : public Icon + class SendSmsIcon : public StateIcon { public: SendSmsIcon() = delete; @@ -38,7 +38,7 @@ namespace gui MUTED }; - class MicrophoneIcon : public Icon + class MicrophoneIcon : public StateIcon { public: MicrophoneIcon() = delete; @@ -51,7 +51,7 @@ namespace gui SPEAKERON }; - class SpeakerIcon : public Icon + class SpeakerIcon : public StateIcon { public: SpeakerIcon() = delete; diff --git a/module-apps/application-call/windows/CallWindow.cpp b/module-apps/application-call/windows/CallWindow.cpp index 6ea7a0878..08aed2313 100644 --- a/module-apps/application-call/windows/CallWindow.cpp +++ b/module-apps/application-call/windows/CallWindow.cpp @@ -6,7 +6,7 @@ #include "GuiTimer.hpp" #include "InputEvent.hpp" -#include "application-call/widgets/Icons.hpp" +#include "application-call/widgets/StateIcons.hpp" #include "log/log.hpp" #include "service-appmgr/Controller.hpp" diff --git a/module-apps/application-call/windows/CallWindow.hpp b/module-apps/application-call/windows/CallWindow.hpp index d4ab62039..a49a481ea 100644 --- a/module-apps/application-call/windows/CallWindow.hpp +++ b/module-apps/application-call/windows/CallWindow.hpp @@ -5,7 +5,7 @@ #include "application-call/ApplicationCall.hpp" #include "AppWindow.hpp" -#include "application-call/widgets/Icons.hpp" +#include "application-call/widgets/StateIcons.hpp" #include #include diff --git a/module-apps/application-call/windows/EnterNumberWindow.hpp b/module-apps/application-call/windows/EnterNumberWindow.hpp index f6dd5c2a6..7458b750e 100644 --- a/module-apps/application-call/windows/EnterNumberWindow.hpp +++ b/module-apps/application-call/windows/EnterNumberWindow.hpp @@ -3,8 +3,8 @@ #pragma once -#include "../ApplicationCall.hpp" -#include "../widgets/Icons.hpp" +#include "application-call/ApplicationCall.hpp" +#include "application-call/widgets/StateIcons.hpp" #include #include diff --git a/module-apps/application-messages/models/SMSTemplateModel.cpp b/module-apps/application-messages/models/SMSTemplateModel.cpp index c5309e958..903a7417a 100644 --- a/module-apps/application-messages/models/SMSTemplateModel.cpp +++ b/module-apps/application-messages/models/SMSTemplateModel.cpp @@ -69,7 +69,7 @@ auto SMSTemplateModel::handleQueryResponse(db::QueryResult *queryResult) -> bool // If list record count has changed we need to rebuild list. if (recordsCount != (msgResponse->getCount())) { recordsCount = msgResponse->getCount(); - list->rebuildList(style::listview::RebuildType::Full, 0, true); + list->reSendLastRebuildRequest(); return false; } diff --git a/module-apps/application-messages/models/SMSThreadModel.cpp b/module-apps/application-messages/models/SMSThreadModel.cpp index b38bf1bd4..54019fd7b 100644 --- a/module-apps/application-messages/models/SMSThreadModel.cpp +++ b/module-apps/application-messages/models/SMSThreadModel.cpp @@ -75,7 +75,7 @@ auto SMSThreadModel::handleQueryResponse(db::QueryResult *queryResult) -> bool if (recordsCount != (msgResponse->getCount() + 1)) { // Additional one element for SMSInputWidget. recordsCount = msgResponse->getCount() + 1; - list->rebuildList(style::listview::RebuildType::Full, 0, true); + list->reSendLastRebuildRequest(); return false; } diff --git a/module-apps/application-messages/models/ThreadsModel.cpp b/module-apps/application-messages/models/ThreadsModel.cpp index 37375d2b9..8d3b70978 100644 --- a/module-apps/application-messages/models/ThreadsModel.cpp +++ b/module-apps/application-messages/models/ThreadsModel.cpp @@ -79,7 +79,7 @@ auto ThreadsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool // If list record count has changed we need to rebuild list. if (recordsCount != (msgResponse->getCount())) { recordsCount = msgResponse->getCount(); - list->rebuildList(style::listview::RebuildType::Full, 0, true); + list->reSendLastRebuildRequest(); return false; } diff --git a/module-apps/application-messages/models/ThreadsSearchResultsModel.cpp b/module-apps/application-messages/models/ThreadsSearchResultsModel.cpp index c27d4f84f..c3e605d66 100644 --- a/module-apps/application-messages/models/ThreadsSearchResultsModel.cpp +++ b/module-apps/application-messages/models/ThreadsSearchResultsModel.cpp @@ -64,7 +64,7 @@ namespace gui::model // If list record count has changed we need to rebuild list. if (recordsCount != (msgResponse->getCount())) { recordsCount = msgResponse->getCount(); - list->rebuildList(style::listview::RebuildType::Full, 0, true); + list->reSendLastRebuildRequest(); return false; } diff --git a/module-apps/application-messages/windows/MessagesMainWindow.cpp b/module-apps/application-messages/windows/MessagesMainWindow.cpp index 0d14d0087..810efc6b1 100644 --- a/module-apps/application-messages/windows/MessagesMainWindow.cpp +++ b/module-apps/application-messages/windows/MessagesMainWindow.cpp @@ -89,13 +89,16 @@ namespace gui return true; }; - emptyListIcon->setVisible(false); - emptyListIcon->focusChangedCallback = [this]([[maybe_unused]] gui::Item &item) { + list->emptyListCallback = [this]() { + emptyListIcon->setVisible(true); bottomBar->setActive(BottomBar::Side::LEFT, false); bottomBar->setActive(BottomBar::Side::CENTER, false); - rightArrowImage->setVisible(false); - searchImage->setVisible(false); - return true; + }; + + list->notEmptyListCallback = [this]() { + emptyListIcon->setVisible(false); + bottomBar->setActive(BottomBar::Side::LEFT, true); + bottomBar->setActive(BottomBar::Side::CENTER, true); }; setFocusItem(list); @@ -125,14 +128,6 @@ namespace gui } } - if (threadsModel->requestRecordsCount() == 0) { - emptyListIcon->setVisible(true); - setFocusItem(emptyListIcon); - } - else { - emptyListIcon->setVisible(false); - } - DBServiceAPI::GetQuery(application, db::Interface::Name::Notifications, std::make_unique(NotificationsRecord::Key::Sms)); diff --git a/module-apps/application-messages/windows/SearchResults.cpp b/module-apps/application-messages/windows/SearchResults.cpp index 00859c42d..1907cdf71 100644 --- a/module-apps/application-messages/windows/SearchResults.cpp +++ b/module-apps/application-messages/windows/SearchResults.cpp @@ -39,6 +39,9 @@ namespace gui msgThreadStyle::listHeight, model); list->setScrollTopMargin(style::margins::small); + setFocusItem(list); + + list->emptyListCallback = [this]() { showEmptyResults(); }; } void SearchResults::onBeforeShow(ShowMode mode, SwitchData *data) diff --git a/module-apps/application-notes/ApplicationNotes.cpp b/module-apps/application-notes/ApplicationNotes.cpp index e8e9a5691..3f9082168 100644 --- a/module-apps/application-notes/ApplicationNotes.cpp +++ b/module-apps/application-notes/ApplicationNotes.cpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace app { @@ -28,7 +29,9 @@ namespace app ApplicationNotes::ApplicationNotes(std::string name, std::string parent, StartInBackground startInBackground) : Application(name, parent, startInBackground, NotesStackSize) - {} + { + busChannels.push_back(sys::BusChannels::ServiceDBNotifications); + } // Invoked upon receiving data message sys::MessagePointer ApplicationNotes::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) @@ -39,6 +42,19 @@ namespace app return retMsg; } + if (msgl->messageType == MessageType::DBServiceNotification) { + auto msg = dynamic_cast(msgl); + if (msg != nullptr) { + // window-specific actions + if (msg->interface == db::Interface::Name::Notes) { + for (auto &[name, window] : windowsStack) { + window->onDatabaseMessage(msg); + } + } + return std::make_shared(); + } + } + if (resp != nullptr) { switch (resp->responseTo) { case MessageType::DBQuery: diff --git a/module-apps/application-notes/model/NotesListModel.cpp b/module-apps/application-notes/model/NotesListModel.cpp index 8d8092f2b..ec438cca0 100644 --- a/module-apps/application-notes/model/NotesListModel.cpp +++ b/module-apps/application-notes/model/NotesListModel.cpp @@ -73,7 +73,7 @@ namespace app::notes { if (recordsCount != notesRepoCount) { recordsCount = notesRepoCount; - list->rebuildList(::style::listview::RebuildType::Full, 0, true); + list->reSendLastRebuildRequest(); return false; } return updateRecords(records); diff --git a/module-apps/application-notes/windows/NoteMainWindow.cpp b/module-apps/application-notes/windows/NoteMainWindow.cpp index 6fed7c62c..64e408a81 100644 --- a/module-apps/application-notes/windows/NoteMainWindow.cpp +++ b/module-apps/application-notes/windows/NoteMainWindow.cpp @@ -19,6 +19,8 @@ #include #include +#include + namespace app::notes { NoteMainWindow::NoteMainWindow(app::Application *app, @@ -103,7 +105,9 @@ namespace app::notes onEmptyList(); return true; }; - emptyListIcon->setVisible(false); + + list->emptyListCallback = [this]() { onEmptyList(); }; + list->notEmptyListCallback = [this]() { onListFilled(); }; setFocusItem(list); } @@ -119,35 +123,11 @@ namespace app::notes emptyListIcon = nullptr; } - void NoteMainWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) - { - if (presenter->isNoteListEmpty()) { - showEmptyIcon(); - } - else { - showList(); - } - } - - void NoteMainWindow::showEmptyIcon() - { - list->setVisible(false); - emptyListIcon->setVisible(true); - setFocusItem(emptyListIcon); - } - - void NoteMainWindow::showList() - { - list->rebuildList(); - list->setVisible(true); - emptyListIcon->setVisible(false); - setFocusItem(list); - } - void NoteMainWindow::onEmptyList() { bottomBar->setActive(gui::BottomBar::Side::LEFT, false); bottomBar->setActive(gui::BottomBar::Side::CENTER, false); + emptyListIcon->setVisible(true); rightArrowImage->setVisible(false); searchImage->setVisible(false); } @@ -156,6 +136,7 @@ namespace app::notes { bottomBar->setActive(gui::BottomBar::Side::LEFT, true); bottomBar->setActive(gui::BottomBar::Side::CENTER, true); + emptyListIcon->setVisible(false); rightArrowImage->setVisible(true); searchImage->setVisible(true); } @@ -174,9 +155,19 @@ namespace app::notes return AppWindow::onInput(inputEvent); } - bool NoteMainWindow::onDatabaseMessage(sys::Message *msgl) + bool NoteMainWindow::onDatabaseMessage(sys::Message *msg) { - auto *msg = static_cast(msgl); - return presenter->updateNotes(std::move(*msg->records)); + auto *msgNotification = dynamic_cast(msg); + if (msgNotification != nullptr) { + if (msgNotification->interface == db::Interface::Name::Notes) { + if (msgNotification->dataModified()) { + + list->rebuildList(::style::listview::RebuildType::InPlace); + + return true; + } + } + } + return false; } } // namespace app::notes diff --git a/module-apps/application-notes/windows/NoteMainWindow.hpp b/module-apps/application-notes/windows/NoteMainWindow.hpp index 3ff14858d..9156ddf93 100644 --- a/module-apps/application-notes/windows/NoteMainWindow.hpp +++ b/module-apps/application-notes/windows/NoteMainWindow.hpp @@ -22,7 +22,6 @@ namespace app::notes // virtual methods bool onInput(const gui::InputEvent &inputEvent) override; - void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override; void rebuild() override; void buildInterface() override; @@ -30,8 +29,6 @@ namespace app::notes bool onDatabaseMessage(sys::Message *msg) override; private: - void showEmptyIcon(); - void showList(); void onEmptyList(); void onListFilled(); diff --git a/module-gui/gui/widgets/ListView.cpp b/module-gui/gui/widgets/ListView.cpp index 87289cdfe..e95bf1efc 100644 --- a/module-gui/gui/widgets/ListView.cpp +++ b/module-gui/gui/widgets/ListView.cpp @@ -4,7 +4,6 @@ #include "ListView.hpp" #include "InputEvent.hpp" #include "cassert" -#include #include namespace gui @@ -99,7 +98,7 @@ namespace gui style::listview::scroll::w, style::listview::scroll::h); - setProvider(prov); + setProvider(std::move(prov)); type = gui::ItemType::LIST; } @@ -144,13 +143,18 @@ namespace gui } } + bool ListView::isEmpty() const noexcept + { + return elementsCount == 0; + } + void ListView::rebuildList(style::listview::RebuildType rebuildType, unsigned int dataOffset, bool forceRebuild) { if (pageLoaded || forceRebuild) { + setElementsCount(provider->requestRecordsCount()); setup(rebuildType, dataOffset); - clearItems(); // If deletion operation caused last page to be removed request previous one. if ((startIndex != 0 && startIndex == elementsCount)) { @@ -163,7 +167,12 @@ namespace gui else { rebuildRequests.push_front({rebuildType, dataOffset}); } - }; + } + + void ListView::reSendLastRebuildRequest() + { + rebuildList(lastRebuildRequest.first, lastRebuildRequest.second, true); + } void ListView::setup(style::listview::RebuildType rebuildType, unsigned int dataOffset) { @@ -185,10 +194,13 @@ namespace gui storedFocusIndex = body->getFocusItemIndex(); if (direction == style::listview::Direction::Top) { - storedFocusIndex = abs((currentPageSize - 1) - storedFocusIndex); + int position = currentPageSize - 1 - storedFocusIndex; + storedFocusIndex = std::abs(position); } } + lastRebuildRequest = {rebuildType, dataOffset}; + body->setReverseOrder(false); direction = style::listview::Direction::Bottom; } @@ -240,6 +252,8 @@ namespace gui return; } + onElementsCountChanged(); + clearItems(); addItemsOnPage(); @@ -348,7 +362,6 @@ namespace gui if (!body->setFocusOnElement(storedFocusIndex)) { body->setFocusOnLastElement(); } - storedFocusIndex = 0; } if (focusOnLastItem) { @@ -357,6 +370,18 @@ namespace gui } }; + void ListView::onElementsCountChanged() + { + if (isEmpty()) { + if (emptyListCallback) { + emptyListCallback(); + } + } + else if (notEmptyListCallback) { + notEmptyListCallback(); + } + } + bool ListView::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) { Rect::onDimensionChanged(oldDim, newDim); @@ -435,7 +460,8 @@ namespace gui direction = style::listview::Direction::Bottom; body->setReverseOrder(false); - pageLoaded = false; + pageLoaded = false; + storedFocusIndex = 0; provider->requestRecords(startIndex, calculateLimit()); return true; @@ -466,7 +492,8 @@ namespace gui direction = style::listview::Direction::Top; body->setReverseOrder(true); - pageLoaded = false; + pageLoaded = false; + storedFocusIndex = 0; provider->requestRecords(topFetchIndex, limit); return true; diff --git a/module-gui/gui/widgets/ListView.hpp b/module-gui/gui/widgets/ListView.hpp index a6a041da3..46d4ef144 100644 --- a/module-gui/gui/widgets/ListView.hpp +++ b/module-gui/gui/widgets/ListView.hpp @@ -14,6 +14,8 @@ namespace gui { class ListItemProvider; + using rebuildRequest = std::pair; + class ListViewScroll : public Rect { public: @@ -28,11 +30,12 @@ namespace gui protected: unsigned int startIndex = 0; unsigned int storedFocusIndex = 0; - unsigned int elementsCount = 1; + unsigned int elementsCount = 0; std::shared_ptr provider = nullptr; VBox *body = nullptr; ListViewScroll *scroll = nullptr; - std::list> rebuildRequests; + std::list rebuildRequests; + rebuildRequest lastRebuildRequest = {style::listview::RebuildType::Full, 0}; unsigned int currentPageSize = 0; bool pageLoaded = true; @@ -52,6 +55,8 @@ namespace gui void fillFirstPage(); void setStartIndex(); void recalculateOnBoxRequestedResize(); + /// Default empty list to inform that there is no elements - callback should be override in applications + void onElementsCountChanged(); unsigned int calculateMaxItemsOnPage(); unsigned int calculateLimit(style::listview::Direction value = style::listview::Direction::Bottom); Order getOrderFromDirection(); @@ -69,6 +74,9 @@ namespace gui void rebuildList(style::listview::RebuildType rebuildType = style::listview::RebuildType::Full, unsigned int dataOffset = 0, bool forceRebuild = false); + /// In case of elements count change there can be a need to resend request in case of having one async query for + /// count and records. + void reSendLastRebuildRequest(); void clear(); std::shared_ptr getProvider(); void setOrientation(style::listview::Orientation value); @@ -77,6 +85,10 @@ namespace gui void setAlignment(const Alignment &value) override; void onProviderDataUpdate(); + [[nodiscard]] bool isEmpty() const noexcept; + std::function emptyListCallback; + std::function notEmptyListCallback; + // virtual methods from Item bool onInput(const InputEvent &inputEvent) override; bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;