gtk.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //#define GI_INLINE 1
  2. #include <gtk/gtk.hpp>
  3. // adapt to API as needed
  4. #if GTK_CHECK_VERSION(4, 0, 0)
  5. #define GTK4 1
  6. #endif
  7. #include <iostream>
  8. #include <tuple>
  9. #include <vector>
  10. namespace GLib = gi::repository::GLib;
  11. namespace GObject_ = gi::repository::GObject;
  12. namespace Gtk = gi::repository::Gtk;
  13. static GLib::MainLoop loop;
  14. // based on python-gtk3 example
  15. // https://python-gtk-3-tutorial.readthedocs.io/en/latest/treeview.html
  16. // list of tuples for each software,
  17. // containing the software name, initial release, and main programming languages
  18. const std::vector<std::tuple<std::string, int, std::string>> software_list{
  19. std::make_tuple("Firefox", 2002, "C++"),
  20. std::make_tuple("Eclipse", 2004, "Java"),
  21. std::make_tuple("Pitivi", 2004, "Python"),
  22. std::make_tuple("Netbeans", 1996, "Java"),
  23. std::make_tuple("Chrome", 2008, "C++"),
  24. std::make_tuple("Filezilla", 2001, "C++"),
  25. std::make_tuple("Bazaar", 2005, "Python"),
  26. std::make_tuple("Git", 2005, "C"),
  27. std::make_tuple("Linux Kernel", 1991, "C"),
  28. std::make_tuple("GCC", 1987, "C"),
  29. std::make_tuple("Frostwire", 2004, "Java")};
  30. class TreeViewFilterWindow : public Gtk::impl::WindowImpl
  31. {
  32. typedef TreeViewFilterWindow self_type;
  33. Gtk::ListStore store_;
  34. Gtk::TreeModelFilter language_filter_;
  35. std::string current_filter_language_;
  36. public:
  37. TreeViewFilterWindow() : Gtk::impl::WindowImpl(this)
  38. {
  39. Gtk::Window &self = *(this);
  40. self.set_title("TreeView filter demo");
  41. #ifdef GTK4
  42. #else
  43. self.set_border_width(10);
  44. #endif
  45. // set up the grid in which elements are positioned
  46. auto grid = Gtk::Grid::new_();
  47. grid.set_column_homogeneous(true);
  48. grid.set_row_homogeneous(true);
  49. #ifdef GTK4
  50. self.set_child(grid);
  51. #if 0
  52. // this could work, but the annotation for .load_from_data
  53. // is too unstable across versions
  54. { // migrate call above to CSS style
  55. const char *css = "grid { margin: 10px; }";
  56. auto provider = Gtk::CssProvider::new_();
  57. provider.load_from_data((guint8 *)css, -1);
  58. grid.get_style_context().add_provider(
  59. provider, Gtk::STYLE_PROVIDER_PRIORITY_USER_);
  60. }
  61. #endif
  62. #else
  63. self.add(grid);
  64. #endif
  65. // create ListStore model
  66. store_ = Gtk::ListStore::new_type_<std::string, int, std::string>();
  67. for (auto &e : software_list) {
  68. auto it = store_.append();
  69. GObject_::Value cols[] = {std::get<0>(e), std::get<1>(e), std::get<2>(e)};
  70. for (unsigned i = 0; i < G_N_ELEMENTS(cols); ++i) {
  71. store_.set_value(it, i, cols[i]);
  72. }
  73. }
  74. // create the filter, feeding it with the liststore model
  75. auto treemodel = store_.interface_(gi::interface_tag<Gtk::TreeModel>());
  76. language_filter_ =
  77. gi::object_cast<Gtk::TreeModelFilter>(treemodel.filter_new(nullptr));
  78. // set the filter function
  79. language_filter_.set_visible_func(
  80. gi::mem_fun(&self_type::language_filter_func, this));
  81. // create the treeview, make it use the filter as a model, and add
  82. // columns
  83. auto treeview = Gtk::TreeView::new_with_model(language_filter_);
  84. int i = 0;
  85. for (auto &e : {"Software", "Release Year", "Programming Language"}) {
  86. auto renderer = Gtk::CellRendererText::new_();
  87. auto column = Gtk::TreeViewColumn::new_(e, renderer, {{"text", i}});
  88. treeview.append_column(column);
  89. ++i;
  90. }
  91. // create buttons to filter by programming language, and set up their
  92. // events
  93. std::vector<Gtk::Widget> buttons;
  94. for (auto &prog_language : {"Java", "C", "C++", "Python", "None"}) {
  95. auto button = Gtk::Button::new_with_label(prog_language);
  96. buttons.push_back(button);
  97. button.signal_clicked().connect(
  98. gi::mem_fun(&self_type::on_selection_button_clicked, this));
  99. }
  100. // set up the layout;
  101. // put the treeview in a scrollwindow, and the buttons in a row
  102. auto scrollable_treelist = Gtk::ScrolledWindow::new_();
  103. scrollable_treelist.set_vexpand(true);
  104. grid.attach(scrollable_treelist, 0, 0, 8, 10);
  105. grid.attach_next_to(
  106. buttons[0], scrollable_treelist, Gtk::PositionType::BOTTOM_, 1, 1);
  107. auto it = buttons.begin() + 1;
  108. while (it != buttons.end()) {
  109. grid.attach_next_to(*it, *(it - 1), Gtk::PositionType::RIGHT_, 1, 1);
  110. ++it;
  111. }
  112. #ifdef GTK4
  113. scrollable_treelist.set_child(treeview);
  114. self.show();
  115. #else
  116. scrollable_treelist.add(treeview);
  117. self.show_all();
  118. #endif
  119. }
  120. bool language_filter_func(Gtk::TreeModel filter, Gtk::TreeIter_Ref it) const
  121. {
  122. if (current_filter_language_.empty() || current_filter_language_ == "None")
  123. return true;
  124. return current_filter_language_ ==
  125. filter.get_value(it, 2).get_value<std::string>();
  126. }
  127. void on_selection_button_clicked(Gtk::Button button)
  128. {
  129. // set the current language filter to the button's label
  130. current_filter_language_ = button.get_label();
  131. std::cout << current_filter_language_ << " language selected!" << std::endl;
  132. // update the filter, which updates in turn the view
  133. language_filter_.refilter();
  134. }
  135. };
  136. // other part based on gtkmm builder example
  137. namespace Gio = gi::repository::Gio;
  138. class ExampleWindow : public Gtk::impl::WindowImpl
  139. {
  140. using self_type = ExampleWindow;
  141. public:
  142. ExampleWindow(Gtk::Window base, Gtk::Builder builder)
  143. : Gtk::impl::WindowImpl(base, this)
  144. {
  145. (void)builder;
  146. auto actions = Gio::SimpleActionGroup::new_();
  147. auto am = Gio::ActionMap(actions);
  148. auto action = Gio::SimpleAction::new_("help");
  149. action.signal_activate().connect(gi::mem_fun(&self_type::on_help, this));
  150. am.add_action(action);
  151. insert_action_group("win", actions);
  152. }
  153. void on_help(Gio::Action, GLib::Variant) { std::cout << "Help" << std::endl; }
  154. static auto build()
  155. {
  156. const char *UIFILE = G_STRINGIFY(EXAMPLES_DIR) "/gtk-builder.ui";
  157. const char *WINID = "window1";
  158. auto builder = Gtk::Builder::new_();
  159. builder.add_from_file(UIFILE);
  160. if (false) {
  161. // some compile checks
  162. builder.get_object(WINID);
  163. builder.get_object<Gtk::Window>(WINID);
  164. }
  165. return builder.get_object_derived<self_type>(WINID);
  166. }
  167. };
  168. int
  169. main(int argc, char **argv)
  170. {
  171. #ifdef GTK4
  172. (void)argc;
  173. (void)argv;
  174. gtk_init();
  175. #else
  176. gtk_init(&argc, &argv);
  177. #endif
  178. loop = GLib::MainLoop::new_();
  179. // recommended general approach iso stack based
  180. // too much vmethod calling which is not safe for plain case
  181. Gtk::Window win;
  182. if (argc == 1) {
  183. win = gi::make_ref<TreeViewFilterWindow>();
  184. } else {
  185. win = ExampleWindow::build();
  186. }
  187. // TODO auto-handle arg ignore ??
  188. #ifdef GTK4
  189. win.signal_close_request().connect([](Gtk::Window) {
  190. loop.quit();
  191. return true;
  192. });
  193. win.show();
  194. #else
  195. win.signal_destroy().connect([](Gtk::Widget) { loop.quit(); });
  196. win.show_all();
  197. #endif
  198. loop.run();
  199. }