The listbox model is a new feature which will be introduced into Nana 1.4. The listbox model is to use an existing STL container and adopt the container as the storage for a listbox category.
//Definition of a person struct person { std::string name; unsigned age; }; //A container stores persons std::vector<person> persons; //Insert 3 persons. persons.push_back(person{"Steve", 20}); persons.push_back(person{"Nolan", 25}); persons.push_back(person{"Susan", 21}); //A listbox to list persons nana::listbox lsbox(form, nana::rectangle{10, 10, 300, 200}); lsbox.append_header("name"); lsbox.append_header("age"); //We have a container and a listbox. //Now we need to defines two functions to make the listbox 'recognize' //the struct person. auto value_translator = [](const std::vector<nana::listbox::cell>& cells) { person p; p.name = cells[0].text; p.age = std::stoul(cells[1].text); return p; }; auto cell_translator = [](const person& p) { std::vector<nana::listbox::cell> cells; cells.emplace_back(p.name); cells.emplace_back(std::to_string(p.age)); return cells; }; //Everything is ready but last step lsbox.at(0).model<std::recursive_mutex>(persons, value_translator, cell_translator);
The listbox displays container elements.
listbox defines 2 different model types.
1, Standalone model owns an internal container. It copys or moves from the actual argument container. The above example illustrates a standalone model, it copys the 'persons'.
Now, it is moved from 'persons'
lsbox.at(0).model<std::recursive_mutex>(std::move(persons), value_translator, cell_translator);
2, Shared model refers to the actual argument container. The following example illustrates the shared model.
lsbox.at(0).shared_model<std::recursive_mutex>(persons, value_translator, cell_translator); lsbox.events().dbl_click([&] { lsb.at(0).append({ "Who", "10000"}); auto size = persons.size(); //'persons' increases after double-click });
Restriction of shared model Don't emplace/insert/emplace_back/erase/remove the referred container. The behavior is unspecified otherwise. For example
lsbox.at(0).shared_model<std::recursive_mutex>(persons, value_translator, cell_translator); lsbox.events().dbl_click([&] { lsb.at(0).append({ "Who", "10000"}); //It's OK persons.push_back(person{"David", 24}); //adding new elements/removing elements to shared model referred container is behavior unspecified! });
A model_guard is used for accessing the container of listbox model. model_guard is a RAII class that locks for atomically accessing model container. The type of lock is specified by the first template parameter of function template shared_model()/model().
lsbox.at(0).shared_model<std::recursive_mutex>(persons, value_translator, cell_translator); lsbox.events().dbl_click([&] { //model_guard(object mdg) will lock the scope to //atomically access model container. auto mdg = lsbox.at(0).model(); auto & cont = mdg.container<std::vector<person>>(); //Loop through 'persons' for(auto & ps : cont) { //ps.age; } //vs for(auto & im : lsbox.at(0)) { //auto age = std::stoi(im.text(1)); } });
If a shared model refers to a container which is declared as const, it throws exception when the listbox performs append/remove an item to prevent modification of the referred constant container.
const auto& immutable_persons = persons; //Just specify a cell translator when set a constant container. lsbox.at(0).shared_model<std::recursive_mutex>(immutable_persons, cell_translator); lsb.at(0).append({ "Who", "10000"}); //throws lsb.erase(lsb.at(0).begin()); //throws auto mdg = lsb.at(0).model(); auto & cont = mg.container<std::vector<person>>(); //throws const auto mdg = lsb.at(0).model(); auto & cont = mg.container<std::vector<person>>(); //OK, the type of cont is const std::vector<person>&.
Any feedback/suggestion is appreciated.
Nana is a free and open-source C++ library. Please help us keep it that way and Donate what you can today! Thank you for your support.
Anonymous
This sounds very very promising!
But making female==false is not very polite ;-) you better use an enum for gender... just in case !
Now seriously, I hope this will come together with the old possibilities, and will not seriously broke the existing API. I think they can exist in parallel. More than that, maybe we will have the possibility to reuse/interchange the old resolver and this new value_translator and cell_translator?
And maybe there will be a function you can call to tell the list that it need to "refresh" the container, to be used after you made a change in the container that invalid references?
BDW: maybe we can invent some sintatic suggar to hide that .at(0). ?... maybe we can add to list an .operator[] ?
Last edit: qPCR4vir 2016-04-08
1,
Thank you for your advice, I have removed the member gender for this example :-)
2,
I/Oresolver have some issues. It does not know what kind of cell should be generated when there are listboxs display type T in different way. For example, listboxA has columns that require cell which is generated by oresolve<<T.a<<T.b, but listboxB has columns that require cell which is generated by oresolve<<T.a<<T.c. It's very hard to define one overload operator<<(oresolve&, const T&) for different requirements.
//Maybe, reuse resolver in this way.
But it can't assign other status for cell. See the 'cell_translator'
3,
The problem is that, there is another container stores item status(background color, foreground color, selected status, checked status, item image). If the data container(model container) erased an element, listbox has no idea which data element is removed and which item status should be removed. Because the relevance between data container and item status container depends on element position.
4,
OK
Last edit: Jinhao 2016-04-10
View and moderate all "blog Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Blog"
Hello,
Is it also possible to make the listbox work in 'virtual' mode. I mean you tell it that there are, say 1.000.000 records and then, to display such a record, it uses a callback system to fetch the values to display.
The application in mind here is to work as a grid for database records where you only load records in chunks as the user of the application scrolls through them.
Last edit: Anonymous 2017-05-10
View and moderate all "blog Discussion" comments posted by this user
Mark all as spam, and block user from posting to "Blog"
Hello. "ravenspoint" here.
I have tried this with nana v1.5.3. It works well.
However, I am having trouble understanding the use case for this which would be simpler, easier to understand and implement, compared to simply passing a reference to the STL container.