-
Notifications
You must be signed in to change notification settings - Fork 336
Using STL Sequence Container
The listbox has provided a component model
since Nana 1.4
, which allows a STL sequence container to be adopted as storage for a listbox category.
#include <nana/gui.hpp>
#include <nana/gui/widgets/listbox.hpp>
#include <mutex>
#include <vector>
//Definition of a person
struct person
{
std::string name;
unsigned age;
};
int main()
{
//A container stores persons
std::vector<person> persons;
persons.push_back(person{ "Steve", 20 });
persons.push_back(person{ "Nolan", 25 });
persons.push_back(person{ "Susan", 21 });
using namespace nana;
form fm;
//A listbox to list persons
listbox list{ fm };
list.append_header("Name");
list.append_header("Age");
//We have a container and a listbox.
//Now defines 2 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
list.at(0).model<std::recursive_mutex>(persons, value_translator, cell_translator);
fm.div("margin=10 list");
fm["list"] << list;
fm.collocate();
fm.show();
exec();
}
Run the program, it shows persons.
There are 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'.
When a container is passed by rvalue-reference, it is moved.
list.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.
//Everything is ready but last step
list.at(0).shared_model<std::recursive_mutex>(persons, value_translator, cell_translator);
list.events().dbl_click([&]
{
list.at(0).append({ "Who", "10000" });
//lock before accessing persons
auto mdg = list.at(0).model();
//'persons' increases after double-click
auto size = persons.size();
});
Restriction of shared model: Don't emplace/insert/emplace_back/erase/remove the referred container. The behavior is unspecified otherwise. For example
list.events().dbl_click([&]
{
list.at(0).append({ "Who", "10000"}); //It's OK
auto mdg = list.at(0).model();
persons.push_back(person{"David", 24}); //adding new elements/removing elements to shared model referred container is behavior unspecified!
});
Appending/inserting a new element to the STL container directly doesn't inform the listbox
which and where the new element is created.
But modifying existing elements is allowed.
list.events().dbl_click([&]
{
auto mdg = list.at(0).model();
persons.front().age = 50; //Allowed
nana::API::refresh_window(list); //Update the listbox for the modification
});
A model_guard
is used for accessing the container of listbox model. model_guard
is a RAII class that locks for atomically accessing the adopted STL container. The type of lock is specified by the first template parameter of function template shared_model()
/model()
.
After a STL container is adopted as storage for a listbox category, it is strongly recommanded to acquire the model_guard
when accessing the STL container. As the above example of shared model, it creates model_guard
before accessing the container persons
. Unless, the STL container isn't accessed in other threads.
model_guard
also provides a function for easily accessing the adopted STL container.
list.events().dbl_click([&]
{
list.at(0).append({ "Who", "10000" });
//lock before access persons
auto mdg = list.at(0).model();
auto & cont = mdg.container<std::vector<person>>(); //cont refers to persons
//Loop through 'persons'
for (auto & ps : cont)
{
//ps.age;
}
//vs
for (auto & im : list.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 apdoted constant container.
const auto& immutable_persons = persons;
//Just specify a cell translator when set a constant container.
list.at(0).shared_model<std::recursive_mutex>(immutable_persons, cell_translator);
list.at(0).append({ "Who", "10000"}); //throws
list.erase(lsb.at(0).begin()); //throws
auto mdg = list.at(0).model();
auto & cont = mg.container<std::vector<person>>(); //throws
const auto mdg = list.at(0).model();
auto & cont = mg.container<std::vector<person>>(); //OK, the type of cont is const std::vector<person>&.