[gtkmm] Gtk::FlowBox, ListBox: Add bind_model() and bind_list_store()



commit ed8a5bcfe94aba8da2ceb14f235986f37ee4e067
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Thu Jun 30 09:45:40 2016 +0200

    Gtk::FlowBox, ListBox: Add bind_model() and bind_list_store()
    
    Most of ListBox::bind_model() by Murray Cumming <murrayc murrayc com>
    Bug #755149

 gtk/src/flowbox.ccg |   31 ++++++++++++
 gtk/src/flowbox.hg  |  121 +++++++++++++++++++++++++++++++++++++++++++++++-
 gtk/src/listbox.ccg |   29 +++++++++++
 gtk/src/listbox.hg  |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 309 insertions(+), 1 deletions(-)
---
diff --git a/gtk/src/flowbox.ccg b/gtk/src/flowbox.ccg
index 9963fea..d24a642 100644
--- a/gtk/src/flowbox.ccg
+++ b/gtk/src/flowbox.ccg
@@ -74,6 +74,26 @@ static void SignalProxy_Sort_gtk_callback_destroy(void* data)
   delete static_cast<Gtk::FlowBox::SlotSort*>(data);
 }
 
+static GtkWidget* proxy_bind_model_create_widget_callback(void* item, void* data)
+{
+  auto& slot = *static_cast<Gtk::FlowBox::SlotCreateWidget<Glib::Object>*>(data);
+  auto cobject = static_cast<GObject*>(item);
+
+  try
+  {
+    // take_copy is true here, because wrap() returns a Glib::RefPtr<Glib::Object>.
+    // cobject will be unreferenced when the RefPtr is deleted.
+    Gtk::Widget* widget = slot(Glib::wrap(cobject, true));
+    if (widget)
+      return widget->gobj();
+  }
+  catch(...)
+  {
+    Glib::exception_handlers_invoke();
+  }
+  return nullptr;
+}
+
 } // anonymous namespace
 
 namespace Gtk
@@ -121,5 +141,16 @@ void FlowBox::unset_sort_func()
   gtk_flow_box_set_sort_func(gobj(), nullptr, nullptr, nullptr);
 }
 
+void FlowBox::bind_model(const Glib::RefPtr<Gio::ListModel>& model,
+  const SlotCreateWidget<Glib::Object>& slot_create_widget)
+{
+  // Create a copy of the slot.
+  // It will be deleted by Glib::destroy_notify_delete<SlotCreateWidget>.
+  auto slot_copy = new SlotCreateWidget<Glib::Object>(slot_create_widget);
+
+  gtk_flow_box_bind_model(gobj(), Glib::unwrap(model), &proxy_bind_model_create_widget_callback,
+    slot_copy, &Glib::destroy_notify_delete<SlotCreateWidget<Glib::Object>>);
+}
+
 } //namespace Gtk
 
diff --git a/gtk/src/flowbox.hg b/gtk/src/flowbox.hg
index 87edd24..65400f0 100644
--- a/gtk/src/flowbox.hg
+++ b/gtk/src/flowbox.hg
@@ -17,7 +17,9 @@
 #include <gtkmm/container.h>
 #include <gtkmm/orientable.h>
 #include <gtkmm/flowboxchild.h>
+#include <giomm/liststore.h>
 #include <vector>
+#include <utility> // std::forward
 
 _DEFS(gtkmm,gtk)
 _PINCLUDE(gtkmm/private/container_p.h)
@@ -213,6 +215,79 @@ public:
 
   _WRAP_METHOD(void invalidate_sort(), gtk_flow_box_invalidate_sort)
 
+  /** For instance:
+   * Gtk::Widget* on_create_widget(const Glib::RefPtr<T_item>& item);
+   *
+   * Called for flow boxes that are bound to a Gio::ListModel with bind_model()
+   * or bind_list_store() for each item that gets added to the model.
+   *
+   * @newin{3,22}
+   *
+   * @tparam T_item Base class of the items in the Gio::ListModel. All items must
+   *                be of type T_item or a type derived from T_item.
+   *                T_item must be Glib::Object or a type derived from Glib::Object.
+   * @param item The item from the model for which to create a widget.
+   * @eturn A Gtk::Widget that represents @a item.
+   */
+  template <typename T_item>
+  using SlotCreateWidget = sigc::slot<Gtk::Widget*, const Glib::RefPtr<T_item>&>;
+
+  /** Binds a Gio::ListModel.
+   *
+   * If this FlowBox was already bound to a model, that previous binding is
+   * destroyed.
+   *
+   * The contents of the FlowBox are cleared and then filled with widgets that
+   * represent items from @a model. The FlowBox is updated whenever @a model changes.
+   * If @a model is an empty Glib::RefPtr, the FlowBox is left empty.
+   *
+   * It is undefined to add or remove widgets directly (for example, with
+   * insert() or Gtk::Container::add()) while the FlowBox is bound to a model.
+   *
+   * Note that using a model is incompatible with the filtering and sorting
+   * functionality in FlowBox. When using a model, filtering and sorting
+   * should be implemented by the model.
+   *
+   * @newin{3,22}
+   *
+   * @param model The Gio::ListModel to be bound.
+   * @param slot_create_widget A slot that creates widgets for items.
+   *
+   * @see bind_list_store()
+   */
+  void bind_model(const Glib::RefPtr<Gio::ListModel>& model,
+    const SlotCreateWidget<Glib::Object>& slot_create_widget);
+  _IGNORE(gtk_flow_box_bind_model)
+
+  /** Binds a Gio::ListStore<>.
+   *
+   * If this FlowBox was already bound to a Gio::ListModel, that previous binding is
+   * destroyed. (Gio::ListStore is a Gio::ListModel.)
+   *
+   * The contents of the FlowBox are cleared and then filled with widgets that
+   * represent items from @a store. The FlowBox is updated whenever @a store changes.
+   * If @a store is an empty Glib::RefPtr, the FlowBox is left empty.
+   *
+   * It is undefined to add or remove widgets directly (for example, with
+   * insert() or Gtk::Container::add()) while the FlowBox is bound to a model.
+   *
+   * Note that using a model is incompatible with the filtering and sorting
+   * functionality in FlowBox. When using a model, filtering and sorting
+   * should be implemented by the model.
+   *
+   * @newin{3,22}
+   *
+   * @tparam T_item Base class of the items in the Gio::ListStore. All items must
+   *                be of type T_item or a type derived from T_item.
+   *                T_item must be Glib::Object or a type derived from Glib::Object.
+   * @tparam T_slot SlotCreateWidget<T_item> or a type that can be converted
+   *                to SlotCreateWidget<T_item>.
+   * @param store The Gio::ListStore<> to be bound.
+   * @param slot_create_widget A slot that creates widgets for items.
+   */
+  template <typename T_item, typename T_slot>
+  void bind_list_store(const Glib::RefPtr<Gio::ListStore<T_item>>& store,
+    T_slot&& slot_create_widget);
 
   _WRAP_PROPERTY("selection-mode", SelectionMode)
   _WRAP_PROPERTY("activate-on-single-click", bool)
@@ -226,7 +301,51 @@ public:
   _WRAP_SIGNAL(void child_activated(FlowBoxChild* child), "child-activated")
   _WRAP_SIGNAL(void selected_children_changed(), "selected-children-changed")
   _IGNORE_SIGNAL(activate-cursor-child, toggle-cursor-child, move-cursor, select-all, unselect-all) // 
Action signals
+
+private:
+  template <typename T_item>
+  static GtkWidget* proxy_bind_list_store_create_widget_callback(void* item, void* data);
 };
 
-} // namespace Gtk
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+template <typename T_item, typename T_slot>
+void FlowBox::bind_list_store(const Glib::RefPtr<Gio::ListStore<T_item>>& store,
+  T_slot&& slot_create_widget)
+{
+  // Create a copy of the slot.
+  // It will be deleted by Glib::destroy_notify_delete<SlotCreateWidget>.
+  auto slot_copy = new SlotCreateWidget<T_item>(std::forward<T_slot>(slot_create_widget));
+
+  gtk_flow_box_bind_model(gobj(),
+    Glib::unwrap(Glib::RefPtr<Gio::ListModel>::cast_static(store)),
+    &proxy_bind_list_store_create_widget_callback<T_item>,
+    slot_copy, &Glib::destroy_notify_delete<SlotCreateWidget<T_item>>);
+}
+
+template <typename T_item>
+GtkWidget* FlowBox::proxy_bind_list_store_create_widget_callback(void* item, void* data)
+{
+  auto& slot = *static_cast<SlotCreateWidget<T_item>*>(data);
+  auto cobject = static_cast<typename T_item::BaseObjectType*>(item);
+
+  try
+  {
+    // cast_dynamic is necessary if T_item is a user-defined type, such as
+    // class MyObject : public Glib::Object
+    // take_copy is true here, because wrap() returns a Glib::RefPtr<>.
+    // cobject will be unreferenced when the RefPtr is deleted.
+    Gtk::Widget* widget = slot(Glib::RefPtr<T_item>::cast_dynamic(Glib::wrap(cobject, true)));
+    if (widget)
+      return widget->gobj();
+  }
+  catch(...)
+  {
+    Glib::exception_handlers_invoke();
+  }
+  return nullptr;
+}
+
+#endif // DOXYGEN_SHOULD_SKIP_THIS
 
+} // namespace Gtk
diff --git a/gtk/src/listbox.ccg b/gtk/src/listbox.ccg
index 69a8118..c68734d 100644
--- a/gtk/src/listbox.ccg
+++ b/gtk/src/listbox.ccg
@@ -95,6 +95,25 @@ static void proxy_foreach_callback(GtkListBox* /* list_box */, GtkListBoxRow* ro
   }
 }
 
+static GtkWidget* proxy_bind_model_create_widget_callback(void* item, void* data)
+{
+  auto& slot = *static_cast<Gtk::ListBox::SlotCreateWidget<Glib::Object>*>(data);
+  auto cobject = static_cast<GObject*>(item);
+
+  try
+  {
+    // take_copy is true here, because wrap() returns a Glib::RefPtr<Glib::Object>.
+    // cobject will be unreferenced when the RefPtr is deleted.
+    Gtk::Widget* widget = slot(Glib::wrap(cobject, true));
+    if (widget)
+      return widget->gobj();
+  }
+  catch(...)
+  {
+    Glib::exception_handlers_invoke();
+  }
+  return nullptr;
+}
 
 } // anonymous namespace
 
@@ -171,7 +190,17 @@ void ListBox::selected_foreach(const SlotForeach& slot)
 {
   SlotForeach slot_copy(slot); //TODO: Is this necessary?
   gtk_list_box_selected_foreach(gobj(), &proxy_foreach_callback, &slot_copy);
+}
+
+void ListBox::bind_model(const Glib::RefPtr<Gio::ListModel>& model,
+  const SlotCreateWidget<Glib::Object>& slot_create_widget)
+{
+  // Create a copy of the slot.
+  // It will be deleted by Glib::destroy_notify_delete<SlotCreateWidget>.
+  auto slot_copy = new SlotCreateWidget<Glib::Object>(slot_create_widget);
 
+  gtk_list_box_bind_model(gobj(), Glib::unwrap(model), &proxy_bind_model_create_widget_callback,
+    slot_copy, &Glib::destroy_notify_delete<SlotCreateWidget<Glib::Object>>);
 }
 
 } //namespace Gtk
diff --git a/gtk/src/listbox.hg b/gtk/src/listbox.hg
index 3bbcc23..b7bd210 100644
--- a/gtk/src/listbox.hg
+++ b/gtk/src/listbox.hg
@@ -17,6 +17,9 @@
 #include <gtkmm/container.h>
 #include <gtkmm/listboxrow.h>
 #include <gtkmm/enums.h>
+#include <giomm/liststore.h>
+#include <vector>
+#include <utility> // std::forward
 
 _DEFS(gtkmm,gtk)
 _PINCLUDE(gtkmm/private/container_p.h)
@@ -40,6 +43,8 @@ class Adjustment;
  * add any kind of widget to it via Container::add(), and a ListBoxRow
  * widget will automatically be inserted between the list and the widget.
  *
+ * Also see FlowBox.
+ *
  * A ListBox looks like this:
  * @image html listbox1.png
  *
@@ -238,6 +243,85 @@ public:
   _WRAP_METHOD(void drag_unhighlight_row(), gtk_list_box_drag_unhighlight_row)
   _WRAP_METHOD(void drag_highlight_row(ListBoxRow& row), gtk_list_box_drag_highlight_row)
 
+  /** For instance:
+   * Gtk::Widget* on_create_widget(const Glib::RefPtr<T_item>& item);
+   *
+   * Called for list boxes that are bound to a Gio::ListModel with bind_model()
+   * or bind_list_store() for each item that gets added to the model.
+   *
+   * Versions of GTK+ prior to 3.18 called show_all() on the rows
+   * created by the SlotCreateWidget, but this forced all widgets
+   * inside the row to be shown, and is no longer the case. Applications should
+   * be updated to show the desired row widgets.
+   *
+   * @newin{3,22}
+   *
+   * @tparam T_item Base class of the items in the Gio::ListModel. All items must
+   *                be of type T_item or a type derived from T_item.
+   *                T_item must be Glib::Object or a type derived from Glib::Object.
+   * @param item The item from the model for which to create a widget.
+   * @eturn A Gtk::Widget that represents @a item.
+   */
+  template <typename T_item>
+  using SlotCreateWidget = sigc::slot<Gtk::Widget*, const Glib::RefPtr<T_item>&>;
+
+  /** Binds a Gio::ListModel.
+   *
+   * If this ListBox was already bound to a model, that previous binding is
+   * destroyed.
+   *
+   * The contents of the ListBox are cleared and then filled with widgets that
+   * represent items from @a model. The ListBox is updated whenever @a model changes.
+   * If @a model is an empty Glib::RefPtr, the ListBox is left empty.
+   *
+   * It is undefined to add or remove widgets directly (for example, with
+   * insert() or Gtk::Container::add()) while the ListBox is bound to a model.
+   *
+   * Note that using a model is incompatible with the filtering and sorting
+   * functionality in ListBox. When using a model, filtering and sorting
+   * should be implemented by the model.
+   *
+   * @newin{3,22}
+   *
+   * @param model The Gio::ListModel to be bound.
+   * @param slot_create_widget A slot that creates widgets for items.
+   *
+   * @see bind_list_store()
+   */
+  void bind_model(const Glib::RefPtr<Gio::ListModel>& model,
+    const SlotCreateWidget<Glib::Object>& slot_create_widget);
+  _IGNORE(gtk_list_box_bind_model)
+
+  /** Binds a Gio::ListStore<>.
+   *
+   * If this ListBox was already bound to a Gio::ListModel, that previous binding is
+   * destroyed. (Gio::ListStore is a Gio::ListModel.)
+   *
+   * The contents of the ListBox are cleared and then filled with widgets that
+   * represent items from @a store. The ListBox is updated whenever @a store changes.
+   * If @a store is an empty Glib::RefPtr, the ListBox is left empty.
+   *
+   * It is undefined to add or remove widgets directly (for example, with
+   * insert() or Gtk::Container::add()) while the ListBox is bound to a model.
+   *
+   * Note that using a model is incompatible with the filtering and sorting
+   * functionality in ListBox. When using a model, filtering and sorting
+   * should be implemented by the model.
+   *
+   * @newin{3,22}
+   *
+   * @tparam T_item Base class of the items in the Gio::ListStore. All items must
+   *                be of type T_item or a type derived from T_item.
+   *                T_item must be Glib::Object or a type derived from Glib::Object.
+   * @tparam T_slot SlotCreateWidget<T_item> or a type that can be converted
+   *                to SlotCreateWidget<T_item>.
+   * @param store The Gio::ListStore<> to be bound.
+   * @param slot_create_widget A slot that creates widgets for items.
+   */
+  template <typename T_item, typename T_slot>
+  void bind_list_store(const Glib::RefPtr<Gio::ListStore<T_item>>& store,
+    T_slot&& slot_create_widget);
+
   _WRAP_PROPERTY("selection-mode", SelectionMode)
   _WRAP_PROPERTY("activate-on-single-click", bool)
 
@@ -248,6 +332,51 @@ public:
   _WRAP_SIGNAL(void selected_rows_changed(), selected-rows-changed, no_default_handler)
 
   _IGNORE_SIGNAL(activate-cursor-row, toggle-cursor-row, move-cursor, select-all, unselect-all) // Action 
signals
+
+private:
+  template <typename T_item>
+  static GtkWidget* proxy_bind_list_store_create_widget_callback(void* item, void* data);
 };
 
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+template <typename T_item, typename T_slot>
+void ListBox::bind_list_store(const Glib::RefPtr<Gio::ListStore<T_item>>& store,
+  T_slot&& slot_create_widget)
+{
+  // Create a copy of the slot.
+  // It will be deleted by Glib::destroy_notify_delete<SlotCreateWidget>.
+  auto slot_copy = new SlotCreateWidget<T_item>(std::forward<T_slot>(slot_create_widget));
+
+  gtk_list_box_bind_model(gobj(),
+    Glib::unwrap(Glib::RefPtr<Gio::ListModel>::cast_static(store)),
+    &proxy_bind_list_store_create_widget_callback<T_item>,
+    slot_copy, &Glib::destroy_notify_delete<SlotCreateWidget<T_item>>);
+}
+
+template <typename T_item>
+GtkWidget* ListBox::proxy_bind_list_store_create_widget_callback(void* item, void* data)
+{
+  auto& slot = *static_cast<SlotCreateWidget<T_item>*>(data);
+  auto cobject = static_cast<typename T_item::BaseObjectType*>(item);
+
+  try
+  {
+    // cast_dynamic is necessary if T_item is a user-defined type, such as
+    // class MyObject : public Glib::Object
+    // take_copy is true here, because wrap() returns a Glib::RefPtr<>.
+    // cobject will be unreferenced when the RefPtr is deleted.
+    Gtk::Widget* widget = slot(Glib::RefPtr<T_item>::cast_dynamic(Glib::wrap(cobject, true)));
+    if (widget)
+      return widget->gobj();
+  }
+  catch(...)
+  {
+    Glib::exception_handlers_invoke();
+  }
+  return nullptr;
+}
+
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
 } // namespace Gtk


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]