Skip to content

Using STL Sequence Container

Jinhao edited this page Jul 31, 2017 · 2 revisions

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.

Basic Usage

#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();
}

Show persons

Run the program, it shows persons.

Two Kind of Models

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
});

Model Guard

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));
	}
});

Exceptions

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>&.