summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjell Ahlstedt <kjellahlstedt@gmail.com>2023-01-16 14:28:13 +0100
committerKjell Ahlstedt <kjellahlstedt@gmail.com>2023-01-16 14:28:13 +0100
commit6a7f6d36715ec91114b27b7568ef482837e8c651 (patch)
treee8e1a73822f550035c0800b0d97b55a56923aa0d
parent368335a2970865e79c16eb0f71b43e88422dce15 (diff)
downloadglibmm-6a7f6d36715ec91114b27b7568ef482837e8c651.tar.gz
Gio::Settings: Add bind() overloads and unbind()
Add the bind() overloads with mapping functions.
-rw-r--r--gio/src/settings.ccg91
-rw-r--r--gio/src/settings.hg266
2 files changed, 353 insertions, 4 deletions
diff --git a/gio/src/settings.ccg b/gio/src/settings.ccg
index 05ff5e21..7343bb64 100644
--- a/gio/src/settings.ccg
+++ b/gio/src/settings.ccg
@@ -19,9 +19,94 @@
#include <glibmm/vectorutils.h>
#include <giomm/settingsschema.h>
+namespace
+{
+struct SettingsMapSlots
+{
+ SettingsMapSlots(const Gio::Settings::SlotGetMapping& get_mapping,
+ const Gio::Settings::SlotSetMapping& set_mapping)
+ : from_setting_to_property(get_mapping), from_property_to_setting(set_mapping)
+ {
+ }
+
+ Gio::Settings::SlotGetMapping from_setting_to_property;
+ Gio::Settings::SlotSetMapping from_property_to_setting;
+};
+
+gboolean
+Settings_get_mapping_callback(
+ GValue* to_value, GVariant* from_variant, gpointer user_data)
+{
+ Gio::Settings::SlotGetMapping& the_slot =
+ static_cast<SettingsMapSlots*>(user_data)->from_setting_to_property;
+
+ bool result = false;
+ try
+ {
+ result = the_slot(to_value, from_variant);
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+ return result;
+}
+
+GVariant*
+Settings_set_mapping_callback(
+ const GValue* from_value, const GVariantType* expected_type, gpointer user_data)
+{
+ Gio::Settings::SlotSetMapping& the_slot =
+ static_cast<SettingsMapSlots*>(user_data)->from_property_to_setting;
+
+ GVariant* result = nullptr;
+ try
+ {
+ result = the_slot(from_value, expected_type);
+ }
+ catch (...)
+ {
+ Glib::exception_handlers_invoke();
+ }
+ return result;
+}
+
+void
+Settings_map_callback_destroy(gpointer user_data)
+{
+ delete static_cast<SettingsMapSlots*>(user_data);
+}
+
+} // anonymous namespace
+
namespace Gio
{
+void Settings::bind_value(const Glib::ustring& key,
+ Glib::ObjectBase* object, const Glib::ustring& property, BindFlags flags,
+ const SlotGetMapping& get_mapping, const SlotSetMapping& set_mapping)
+{
+ if (get_mapping.empty() && set_mapping.empty())
+ {
+ // No user-supplied mappings.
+ g_settings_bind(gobj(), key.c_str(), object->gobj(),
+ property.c_str(), (GSettingsBindFlags)flags);
+ }
+ else
+ {
+ // Create copies of the slots. A pointer to this will be passed
+ // through the callback's data parameter. It will be deleted
+ // when Settings_map_callback_destroy() is called.
+ SettingsMapSlots* slots_copy = new SettingsMapSlots(get_mapping, set_mapping);
+
+ g_settings_bind_with_mapping(gobj(), key.c_str(),
+ object->gobj(), property.c_str(), (GSettingsBindFlags)flags,
+ get_mapping.empty() ? nullptr : &Settings_get_mapping_callback,
+ set_mapping.empty() ? nullptr : &Settings_set_mapping_callback, slots_copy,
+ &Settings_map_callback_destroy);
+ }
+}
+
void
Settings::get_value(const Glib::ustring& key, Glib::VariantBase& value) const
{
@@ -66,4 +151,10 @@ Settings::bind_writable(
bind_writable(key, property_proxy.get_object(), property_proxy.get_name(), inverted);
}
+//static
+void Settings::unbind(const Glib::PropertyProxy_Base& property_proxy)
+{
+ unbind(property_proxy.get_object(), property_proxy.get_name());
+}
+
}
diff --git a/gio/src/settings.hg b/gio/src/settings.hg
index d08260d2..66c8b4d5 100644
--- a/gio/src/settings.hg
+++ b/gio/src/settings.hg
@@ -176,17 +176,190 @@ public:
_WRAP_METHOD(std::vector<Glib::ustring> list_children() const, g_settings_list_children)
_IGNORE(g_settings_list_keys)
- _IGNORE(g_settings_get_range, g_settings_list_relocatable_schemas) // deprecated
+ _IGNORE(g_settings_get_range, g_settings_list_relocatable_schemas) dnl// deprecated
_IGNORE(g_settings_range_check)
#m4 _CONVERSION(`Glib::ObjectBase*',`gpointer',(gpointer)$3->gobj())
_WRAP_METHOD(void bind(const Glib::ustring& key, Glib::ObjectBase* object, const Glib::ustring& property, BindFlags flags = BindFlags::DEFAULT), g_settings_bind)
+
+ /** Create a binding between the @a key in the @a settings object
+ * and the @a property_proxy.
+ *
+ * The binding uses the default GIO mapping functions to map
+ * between the settings and property values. These functions
+ * handle booleans, numeric types and string types in a
+ * straightforward way. Use g_settings_bind_with_mapping() if
+ * you need a custom mapping, or map between types that are not
+ * supported by the default mapping functions.
+ *
+ * Unless the @a flags include Gio::Settings::BindFlags::NO_SENSITIVITY, this
+ * function also establishes a binding between the writability of
+ * @a key and the "sensitive" property of @a object (if @a object has
+ * a boolean property by that name). See bind_writable()
+ * for more details about writable bindings.
+ *
+ * Note that the lifecycle of the binding is tied to @a object,
+ * and that you can have only one binding per object property.
+ * If you bind the same property twice on the same object, the second
+ * binding overrides the first one.
+ *
+ * @param key The key to bind.
+ * @param property_proxy The property to bind.
+ * @param flags Flags for the binding.
+ */
void bind(const Glib::ustring& key, const Glib::PropertyProxy_Base& property_proxy, BindFlags flags = BindFlags::DEFAULT);
- // TODO: implement bind_with_mapping
+
+ /** A slot to be called to map values in a binding.
+ *
+ * For instance:
+ * @code
+ * std::optional<int> map_ustring_to_int(const Glib::ustring& from_string);
+ * @endcode
+ *
+ * @return A value of type <tt>T_to</tt> if the mapping was successful,
+ * and an empty optional with no value (i.e. <tt>std::nullopt</tt>) otherwise.
+ */
+ template <typename T_from, typename T_to>
+ using SlotTypedMapping = sigc::slot<std::optional<T_to>(const T_from&)>;
+
+ // Gio::Settings::bind() is similar to Glib::Binding::bind_property().
+ /** Create a binding between the @a key in the @a settings object
+ * and the property @a property of @a object.
+ *
+ * The binding uses the provided mapping functions to map between
+ * settings and property values.
+ *
+ * Note that the lifecycle of the binding is tied to @a object,
+ * and that you can have only one binding per object property.
+ * If you bind the same property twice on the same object, the second
+ * binding overrides the first one.
+ *
+ * The template parameters <tt>T_setting</tt> and <tt>T_property</tt> must be
+ * explicitly specified in each call. The compiler can't deduce them.
+ * For instance:
+ * @code
+ * m_settings->bind<Glib::ustring, unsigned int>("transition",
+ * m_transition, "selected", Gio::Settings::BindFlags::DEFAULT,
+ * sigc::mem_fun(*this, &ExampleAppPrefs::map_from_ustring_to_int),
+ * sigc::mem_fun(*this, &ExampleAppPrefs::map_from_int_to_ustring));
+ * @endcode
+ *
+ * @newin{2,76}
+ *
+ * @param key The key to bind.
+ * @param object A Glib::ObjectBase.
+ * @param property The name of the property to bind.
+ * @param flags Flags for the binding.
+ * @param slot_get_mapping A function that gets called to convert values from
+ * setting to parameter, or an empty slot to use the default GIO mapping.
+ * @param slot_set_mapping A function that gets called to convert values from
+ * parameter to setting, or an empty slot to use the default GIO mapping.
+ *
+ * @tparam T_setting Type of the setting. Must be a type that can be
+ * stored in a Glib::Variant<T_setting> object.
+ * @tparam T_property Type of the property. Must be a type that can be
+ * stored in a Glib::Value<T_property> object.
+ */
+ template <typename T_setting, typename T_property>
+ void bind(const Glib::ustring& key,
+ Glib::ObjectBase* object, const Glib::ustring& property, BindFlags flags,
+ const SlotTypedMapping<T_setting, T_property>& slot_get_mapping,
+ const SlotTypedMapping<T_property, T_setting>& slot_set_mapping)
+ {
+ return bind_value(key, object, property, flags,
+ slot_get_mapping.empty() ? SlotGetMapping() : GetMappingProp<T_setting, T_property>(slot_get_mapping),
+ slot_set_mapping.empty() ? SlotSetMapping() : SetMappingProp<T_property, T_setting>(slot_set_mapping));
+ }
+
+ /** Create a binding between the @a key in the @a settings object
+ * and the @a property_proxy.
+ *
+ * The binding uses the provided mapping functions to map between
+ * settings and property values.
+ *
+ * Note that the lifecycle of the binding is tied to @a property_proxy's
+ * object, and that you can have only one binding per object property.
+ * If you bind the same property twice on the same object, the second
+ * binding overrides the first one.
+ *
+ * The template parameters <tt>T_setting</tt> and <tt>T_property</tt> must be
+ * explicitly specified in each call. The compiler can't deduce them.
+ * For instance:
+ * @code
+ * m_settings->bind<Glib::ustring, unsigned int>("transition",
+ * m_transition->property_selected(), Gio::Settings::BindFlags::DEFAULT,
+ * sigc::mem_fun(*this, &ExampleAppPrefs::map_from_ustring_to_int),
+ * sigc::mem_fun(*this, &ExampleAppPrefs::map_from_int_to_ustring));
+ * @endcode
+ *
+ * @newin{2,76}
+ *
+ * @param key The key to bind.
+ * @param property_proxy The property to bind.
+ * @param flags Flags for the binding.
+ * @param slot_get_mapping A function that gets called to convert values from
+ * setting to parameter, or an empty slot to use the default GIO mapping.
+ * @param slot_set_mapping A function that gets called to convert values from
+ * parameter to setting, or an empty slot to use the default GIO mapping.
+ *
+ * @tparam T_setting Type of the setting. Must be a type that can be
+ * stored in a Glib::Variant<T_setting> object.
+ * @tparam T_property Type of the property. Must be a type that can be
+ * stored in a Glib::Value<T_property> object.
+ */
+ template <typename T_setting, typename T_property>
+ void bind(const Glib::ustring& key,
+ const Glib::PropertyProxy_Base& property_proxy, BindFlags flags,
+ const SlotTypedMapping<T_setting, T_property>& slot_get_mapping,
+ const SlotTypedMapping<T_property, T_setting>& slot_set_mapping)
+ {
+ bind<T_setting, T_property>(key, property_proxy.get_object(),
+ property_proxy.get_name(), flags, slot_get_mapping, slot_set_mapping);
+ }
+ _IGNORE(g_settings_bind_with_mapping)
+
_WRAP_METHOD(void bind_writable(const Glib::ustring& key, Glib::ObjectBase* object, const Glib::ustring& property, bool inverted=false), g_settings_bind_writable)
+
+ /** Create a binding between the writability of @a key in the
+ * @a settings object and the @a property_proxy.
+ *
+ * The property must be boolean; "sensitive" or "visible"
+ * properties of widgets are the most likely candidates.
+ *
+ * Writable bindings are always uni-directional; changes of the
+ * writability of the setting will be propagated to the object
+ * property, not the other way.
+ *
+ * When the @a inverted argument is <tt>true</tt>, the binding inverts the
+ * value as it passes from the setting to the object, i.e. the property
+ * will be set to <tt>true</tt> if the key is not writable.
+ *
+ * Note that the lifecycle of the binding is tied to @a object,
+ * and that you can have only one binding per object property.
+ * If you bind the same property twice on the same object, the second
+ * binding overrides the first one.
+ *
+ * @param key The key to bind.
+ * @param property_proxy The boolean property to bind.
+ * @param inverted Whether to 'invert' the value.
+ */
void bind_writable(const Glib::ustring& key, const Glib::PropertyProxy_Base& property_proxy, bool inverted=false);
- // TODO: unbind is not actually a class method
+
+ _WRAP_METHOD(static void unbind(Glib::ObjectBase* object, const Glib::ustring& property),
+ g_settings_unbind, newin "2,76")
+
+ /** Removes an existing binding for @a property_proxy.
+ *
+ * Note that bindings are automatically removed when the
+ * object is finalized, so it is rarely necessary to call this
+ * function.
+ *
+ * @newin{2,76}
+ *
+ * @param property_proxy The property whose binding is removed.
+ */
+ static void unbind(const Glib::PropertyProxy_Base& property_proxy);
_WRAP_METHOD(Glib::RefPtr<Action> create_action(const Glib::ustring& key), g_settings_create_action)
@@ -207,6 +380,91 @@ public:
_WRAP_SIGNAL(bool writable_change_event(GQuark key), "writable-change-event")
_WRAP_SIGNAL(void writable_changed(const Glib::ustring& key), writable_changed, detail_name key)
-};
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ // SlotGetMapping and SlotSetMapping must be public. They are used in
+ // the anonymous namespace in settings.ccg.
+ /* A slot to be called to map values in a binding created by bind_value().
+ *
+ * For instance:
+ * @code
+ * bool map_from_setting_to_property(GValue* to_value, GVariant* from_variant);
+ * @endcode
+ *
+ * @return <tt>true</tt> if the mapping was successful, and <tt>false</tt> otherwise.
+ */
+ using SlotGetMapping = sigc::slot<bool(GValue*, GVariant*)>;
+
+ /* A slot to be called to map values in a binding created by bind_value().
+ *
+ * For instance:
+ * @code
+ * GVariant* map_from_property_to_setting(
+ * const GValue* from_value, const GVariantType* expected_type);
+ * @endcode
+ *
+ * @return A new <tt>GVariant</tt> if the mapping was successful,
+ * and <tt>nullptr</tt> otherwise.
+ */
+ using SlotSetMapping = sigc::slot<GVariant*(const GValue*, const GVariantType*)>;
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
+private:
+ void bind_value(const Glib::ustring& key,
+ Glib::ObjectBase* object, const Glib::ustring& property, BindFlags flags,
+ const SlotGetMapping& get_mapping, const SlotSetMapping& set_mapping);
+
+ // The functor GetMappingProp can be implicitly converted to a SlotGetMapping
+ // and used in a call to bind_value().
+ template <typename T_from, typename T_to>
+ class GetMappingProp
+ {
+ public:
+ explicit GetMappingProp(const SlotTypedMapping<T_from, T_to>& slot) : typed_mapping(slot) {}
+
+ bool operator()(GValue* to_value, GVariant* from_variant)
+ {
+ Glib::Variant<T_from> from_glib_variant(from_variant, true);
+ const auto to = typed_mapping(from_glib_variant.get());
+
+ if (!to.has_value())
+ return false;
+
+ Glib::Value<T_to> to_glib_value;
+ to_glib_value.init(to_value);
+ to_glib_value.set(*to);
+ g_value_copy(to_glib_value.gobj(), to_value);
+ return true;
+ }
+
+ private:
+ SlotTypedMapping<T_from, T_to> typed_mapping;
+ }; // Settings::GetMappingProp
+
+ // The functor SetMappingProp can be implicitly converted to a SlotGetMapping
+ // and used in a call to bind_value().
+ template <typename T_from, typename T_to>
+ class SetMappingProp
+ {
+ public:
+ explicit SetMappingProp(const SlotTypedMapping<T_from, T_to>& slot) : typed_mapping(slot) {}
+
+ GVariant* operator()(const GValue* from_value, const GVariantType* /* expected_type */)
+ {
+ Glib::Value<T_from> from_glib_value;
+ from_glib_value.init(from_value);
+ const auto to = typed_mapping(from_glib_value.get());
+
+ if (!to.has_value())
+ return nullptr;
+
+ auto to_glib_variant = Glib::Variant<T_to>::create(*to);
+ return to_glib_variant.gobj_copy();
+ }
+
+ private:
+ SlotTypedMapping<T_from, T_to> typed_mapping;
+ }; // Settings::SetMappingProp
+}; // Settings
} // namespace Gio