Gtk::TreeViewでリスト表示をする
C++で GTKアプリケーションを作る例が世の中に少なすぎてサンプルを探すのに苦労するので自分でネットに上げておく
「[ツリービュー](https://developer-old.gnome.org/gtkmm/stable/group__TreeView.html)でリスト表示したい三銃士を連れてきたよ」
「えっ、[C++でGTK](https://www.gtkmm.org/en/)を!?」
「インタラクション担当、[Gtk::TreeView](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeView.html)」
「型定義担当、[Gtk::TreeModelColumnRecord](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumnRecord.html)」
「データストア担当、[Gtk::ListStore](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1ListStore.html)」
カラム数分の [Gtk::TreeModelColumn<T>](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumn.html) を作って Gtk::TreeModelColumnRecordオブジェクトに [add](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeModelColumnRecord.html#a7084fc10fa1abf0d7317558b8462559f)したらそれを使って [Gtk::ListStoreを構築](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1ListStore.html#aaa6c530560f66872482a0650739276c5)、さらにそれを Gtk::TreeView に [set_model](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeView.html#a35e73786232a17c338b68223ef05b3c3)する。さらに TreeViewに対してもカラムごとに[append_columnでモデルとの紐付けと当時にタイトルの設定をする](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeView.html#aefe3902c11a056a6f0630eced66613eb)必要がある。[行の追加操作](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1ListStore.html#af47307c865cc5de0856fe3f8571fc681)や[イテレーション](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeModel.html#a7697e287ce17c6f2e900c5762f7ba77a)などは ListStoreに対して行う。
カラムの型によって自動的にそれ用のレンダラーが選択されるあたり、張り切ってC++していると言える。(boolのカラムに注目)
ユーザーがリストのアイテムを選択した時のイベントを拾うには [TreeViewの signal_cursor_changed](https://developer-old.gnome.org/gtkmm/stable/classGtk_1_1TreeView.html#afeff5112c4445837ff4d0921acc20ef3) シグナルを使う。

```c++
// GTK4
#include <iostream>
#include <gtkmm.h>
class MainWindow : public Gtk::Window {
Gtk::Box box;
Gtk::Button button;
Gtk::TreeView treeview;
Gtk::TreeModelColumnRecord columnrecoed;
struct {
Gtk::TreeModelColumn<std::string> col1;
Gtk::TreeModelColumn<bool> col2;
Gtk::TreeModelColumn<int> col3;
} columns;
Glib::RefPtr<Gtk::ListStore> liststore;
public:
MainWindow();
virtual void on_realize();
void erase_items_have_even_num();
};
MainWindow::MainWindow() : box(Gtk::Orientation::VERTICAL)
{
columnrecoed.add(columns.col1);
columnrecoed.add(columns.col2);
columnrecoed.add(columns.col3);
liststore = Gtk::ListStore::create(columnrecoed);
treeview.set_model(liststore);
treeview.append_column("文字列", columns.col1);
treeview.append_column("真偽値", columns.col2);
treeview.append_column("整数", columns.col3);
treeview.signal_cursor_changed().connect([this]() {
auto selected_row = treeview.get_selection()->get_selected();
if (selected_row) {
std::cout << selected_row->get_value(columns.col1) << " selected." << std::endl;
}
});
box.append(treeview);
button.set_label("偶数のやつを消す");
button.signal_clicked().connect([this]() { erase_items_have_even_num(); });
box.append(button);
set_child(box);
}
void MainWindow::on_realize()
{
Gtk::Window::on_realize();
liststore->clear();
auto row = *liststore->append();
row[columns.col1] = std::string("ぼぎゃー");
row[columns.col2] = true;
row[columns.col3] = 123;
row = *liststore->append();
row[columns.col1] = std::string("もげー");
row[columns.col2] = false;
row[columns.col3] = 456;
}
void MainWindow::erase_items_have_even_num()
{
auto i = liststore->get_iter("0");
while (i) {
if (i->get_value(columns.col3) % 2 == 0) {
i = liststore->erase(i);
} else {
i++;
}
}
}
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create("com.walbrix.simple_list_using_treeview");
return app->make_window_and_run<MainWindow>(argc, argv);
}
// g++ -std=c++20 -o simple_list_using_treeview simple_list_using_treeview.cpp `pkg-config --cflags --libs gtkmm-4.0`
```