From dcd137a542ffbde148e47d28d521007ffd0c04a3 Mon Sep 17 00:00:00 2001 From: Kjell Ahlstedt Date: Tue, 7 Mar 2023 13:29:33 +0100 Subject: Gio::ListModel: Add get_typed_object() Inspired by gtkmm#132. A test case has been added at gtkmm/tests/filedialog. It's difficult to make a meaningful test case without involving gtkmm. A good test case shall contain a GListModel with objects of a class which has no corresponding C++ class, and implements an interface which is wrapped in a C++ class. --- gio/src/listmodel.hg | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/gio/src/listmodel.hg b/gio/src/listmodel.hg index 32fb183e..33d432fb 100644 --- a/gio/src/listmodel.hg +++ b/gio/src/listmodel.hg @@ -97,6 +97,54 @@ public: _WRAP_METHOD(Glib::RefPtr get_object(guint position), g_list_model_get_object, newin "2,50") _WRAP_METHOD(Glib::RefPtr get_object(guint position) const, g_list_model_get_object, constversion, newin "2,50") + /** Get the item at @a position. + * + * If @a position is greater than the number of items in @a list, + * an empty RefPtr is returned. + * + * @code + * Glib::RefPtr item = list_model->get_typed_object(position); + * @endcode + * is often equivalent to + * @code + * Glib::RefPtr item = std::dynamic_pointer_cast(list_model->get_object(position)); + * @endcode + * + * If T_item is an interface, and the underlying C object is an instance of + * a C class that implements that interface, but there is no corresponding + * C++ class, %get_typed_object() may manage to fetch the item even if + * %get_object() fails and returns an empty RefPtr. + * + * @see get_n_items() and get_object() + * + * @newin{2,76} + * + * @tparam T_item The item to fetch must be of type T_item or a type derived + * from T_item, otherwise an empty RefPtr is returned. T_item must + * be Glib::ObjectBase or a type derived from Glib::ObjectBase. + * + * @param position The position of the item to fetch. + * @return The object at @a position. + */ + template + Glib::RefPtr get_typed_object(guint position); + + /** Get the item at @a position. + * + * See the non-const version. + * + * @newin{2,76} + * + * @tparam T_item The item to fetch must be of type T_item or a type derived + * from T_item, otherwise an empty RefPtr is returned. T_item must + * be Glib::ObjectBase or a type derived from Glib::ObjectBase. + * + * @param position The position of the item to fetch. + * @return The object at @a position. + */ + template + Glib::RefPtr get_typed_object(guint position) const; + _WRAP_SIGNAL(void items_changed(guint position, guint removed, guint added), "items-changed", no_default_handler, newin "2,50") protected: @@ -105,4 +153,52 @@ protected: _WRAP_VFUNC(gpointer get_item(guint position), "get_item") }; +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Glib::RefPtr ListModel::get_typed_object(guint position) +{ + static_assert(std::is_base_of_v, + "T_item must be Glib::ObjectBase or derived from Glib::ObjectBase."); + + // g_list_model_get_object() gives us a reference. + GObject* c_item = g_list_model_get_object(gobj(), position); + if (!c_item) + return {}; + T_item* cpp_item = nullptr; + + // If the C item is an object of a type that is not wrapped in C++ code, + // a call to Glib::wrap_auto() will fail. Glib::wrap_auto_interface() + // succeeds, if T_item is an interface. + if constexpr (std::is_base_of_v && + !std::is_base_of_v) + { + // Call wrap_auto_interface() only if c_item implements T_item::get_base_type(). + // wrap_auto_interface() can create a C++ wrapper of type T_item even if + // the object does not implement that interface. + if (g_type_is_a(G_OBJECT_TYPE(c_item), T_item::get_base_type())) + cpp_item = Glib::wrap_auto_interface(c_item); + else + g_warning("Type %s does not implement the %s interface.", + G_OBJECT_TYPE_NAME(c_item), g_type_name(T_item::get_base_type())); + } + else + cpp_item = dynamic_cast(Glib::wrap_auto(c_item)); + + if (!cpp_item) + g_object_unref(c_item); + return Glib::make_refptr_for_instance(cpp_item); +} + +template +Glib::RefPtr ListModel::get_typed_object(guint position) const +{ + static_assert(std::is_base_of_v, + "T_item must be Glib::ObjectBase or derived from Glib::ObjectBase."); + + return const_cast(this)->get_typed_object(position); +} + +#endif // DOXYGEN_SHOULD_SKIP_THIS + } // namespace Gio -- cgit v1.2.1