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";
+}