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(