[gtkmm: 1/2] Add Gtk::Expression
- From: Andreas Persson <andreasp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtkmm: 1/2] Add Gtk::Expression
- Date: Sun, 20 Sep 2020 08:23:45 +0000 (UTC)
commit d6594f78bb7cb5d9184d0ff37f6c31e889d4cbb4
Author: Andreas Persson <andreasp56 outlook com>
Date: Sun Sep 20 10:18:37 2020 +0200
Add Gtk::Expression
Add binding of gtkexpression. It is implemented as a template with the
value type as a template parameter. Creating a closure expression from a
slot, with other expressions as parameters, is possible, and the types
are checked at compile time.
.gitignore | 4 +
gtk/gtkmm/meson.build | 2 +
gtk/src/dropdown.hg | 6 +-
gtk/src/expression.ccg | 37 +++
gtk/src/expression.hg | 633 +++++++++++++++++++++++++++++++++++++++++
gtk/src/expressionwatch.ccg | 17 ++
gtk/src/expressionwatch.hg | 82 ++++++
gtk/src/filelist.am | 2 +
gtk/src/gtk_docs_override.xml | 32 +++
gtk/src/gtk_extra_objects.defs | 6 +
gtk/src/stringfilter.hg | 12 +-
gtk/src/stringsorter.hg | 14 +-
tools/m4/convert_gtk.m4 | 3 +
13 files changed, 836 insertions(+), 14 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 1dd182d3..f31b4327 100644
--- a/.gitignore
+++ b/.gitignore
@@ -295,6 +295,10 @@ gtk/gtkmm/eventcontrollerscroll.cc
gtk/gtkmm/eventcontrollerscroll.h
gtk/gtkmm/expander.cc
gtk/gtkmm/expander.h
+gtk/gtkmm/expression.cc
+gtk/gtkmm/expression.h
+gtk/gtkmm/expressionwatch.cc
+gtk/gtkmm/expressionwatch.h
gtk/gtkmm/filechooser.cc
gtk/gtkmm/filechooser.h
gtk/gtkmm/filechooserbutton.cc
diff --git a/gtk/gtkmm/meson.build b/gtk/gtkmm/meson.build
index 2bade12e..f6519ec0 100644
--- a/gtk/gtkmm/meson.build
+++ b/gtk/gtkmm/meson.build
@@ -104,6 +104,8 @@ gtkmm_any_hg_ccg_basenames = [
'eventcontrollermotion',
'eventcontrollerscroll',
'expander',
+ 'expression',
+ 'expressionwatch',
'filechooser',
'filechooserbutton',
'filechooserdialog',
diff --git a/gtk/src/dropdown.hg b/gtk/src/dropdown.hg
index 3bf80d95..9a26314b 100644
--- a/gtk/src/dropdown.hg
+++ b/gtk/src/dropdown.hg
@@ -16,6 +16,7 @@
#include <giomm/listmodel.h>
#include <gtkmm/listitemfactory.h>
+#include <gtkmm/expression.h>
#include <gtkmm/widget.h>
_DEFS(gtkmm,gtk)
@@ -58,7 +59,7 @@ class GTKMM_API DropDown : public Widget
_STRUCT_NOT_HIDDEN
public:
- _CTOR_DEFAULT
+ _WRAP_CTOR(DropDown(const Glib::RefPtr<Gio::ListModel>& model = {}, const
Glib::RefPtr<Expression<Glib::ustring>>& expression = {}), gtk_drop_down_new)
/** Creates a new %DropDown that is populated with the strings in @a strings.
*
@@ -84,6 +85,8 @@ public:
_WRAP_METHOD(void set_list_factory(const Glib::RefPtr<ListItemFactory>& factory),
gtk_drop_down_set_list_factory)
_WRAP_METHOD(Glib::RefPtr<ListItemFactory> get_list_factory(), gtk_drop_down_get_list_factory, refreturn)
_WRAP_METHOD(Glib::RefPtr<const ListItemFactory> get_list_factory() const, gtk_drop_down_get_list_factory,
refreturn, constversion)
+ _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression),
gtk_drop_down_set_expression)
+ _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const, gtk_drop_down_get_expression)
_WRAP_METHOD(void set_enable_search(bool enable_search = true), gtk_drop_down_set_enable_search)
_WRAP_METHOD(bool get_enable_search() const, gtk_drop_down_get_enable_search)
@@ -94,6 +97,7 @@ public:
_WRAP_PROPERTY("selected", guint)
_WRAP_PROPERTY("selected-item", Glib::RefPtr<Glib::ObjectBase>)
_WRAP_PROPERTY("enable-search", bool)
+ _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
};
} // namespace Gtk
diff --git a/gtk/src/expression.ccg b/gtk/src/expression.ccg
new file mode 100644
index 00000000..005e6e46
--- /dev/null
+++ b/gtk/src/expression.ccg
@@ -0,0 +1,37 @@
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/expression.h>
+
+
+namespace Gtk
+{
+
+namespace Expression_Private
+{
+
+void watch_callback(gpointer data)
+{
+ auto slot = static_cast<sigc::slot<void()>*>(data);
+ (*slot)();
+}
+
+void closure_callback_func()
+{}
+
+} // namespace Expression_Private
+
+} // namespace Gtk
diff --git a/gtk/src/expression.hg b/gtk/src/expression.hg
new file mode 100644
index 00000000..2815f62e
--- /dev/null
+++ b/gtk/src/expression.hg
@@ -0,0 +1,633 @@
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/expressionwatch.h>
+
+_DEFS(gtkmm,gtk)
+
+namespace Gtk
+{
+
+/** Base class for Gtk::Expression.
+ *
+ * @see Gtk::Expression
+ *
+ * @newin{3,98}
+ */
+class GTKMM_API ExpressionBase
+{
+ _CLASS_OPAQUE_REFCOUNTED(ExpressionBase, GtkExpression, NONE, gtk_expression_ref, gtk_expression_unref,
GTKMM_API)
+ _IGNORE(gtk_expression_ref, gtk_expression_unref, gtk_expression_bind, gtk_expression_evaluate,
gtk_expression_watch)
+
+public:
+
+ /** For instance: void on_notify();
+ *
+ * Callback called by Gtk::Expression::watch() when the expression
+ * value changes.
+ */
+ using SlotNotify = sigc::slot<void()>;
+
+ _WRAP_METHOD(GType get_value_type() const, gtk_expression_get_value_type)
+ _WRAP_METHOD(bool is_static() const, gtk_expression_is_static)
+};
+
+/** Expressions to values.
+ *
+ * %Gtk::Expression provides a way to describe references to values.
+ *
+ * An important aspect of expressions is that the value can be obtained
+ * from a source that is several steps away. For example, an expression
+ * may describe ‘the value of property A of @a object1, which is itself the
+ * value of a property of @a object2’. And @a object1 may not even exist yet
+ * at the time that the expression is created. This is contrast to
+ * Glib::Binding, which can only create direct connections between
+ * the properties of two objects that must both exist for the duration
+ * of the binding.
+ *
+ * An expression needs to be "evaluated" to obtain the value that it currently
+ * refers to. An evaluation always happens in the context of a current object
+ * called `this` (it mirrors the behavior of object-oriented languages),
+ * which may or may not influence the result of the evaluation. Use
+ * evaluate() for evaluating an expression.
+ *
+ * Various methods for defining expressions exist, from simple constants via
+ * Gtk::ConstantExpression() to looking up properties in an object (even
+ * recursively) via Gtk::PropertyExpression() or providing custom functions
+ * to transform and combine expressions via Gtk::ClosureExpression().
+ *
+ * Here is an example of a complex expression:
+ * ~~~
+ * color_expr = Gtk::PropertyExpression<Glib::RefPtr<Glib::ObjectBase>>::create(
+ * Gtk::ListItem::get_type(), "item");
+ * expression = Gtk::PropertyExpression<Glib::ustring>::create(
+ * GTK_TYPE_COLOR, color_expr, "name");
+ * ~~~
+ * when evaluated with `this` being a Gtk::ListItem, it will obtain the
+ * "item" property from the Gtk::ListItem, and then obtain the "name" property
+ * from the resulting object (which is assumed to be of type GTK_TYPE_COLOR).
+ *
+ * A more concise way to describe this would be
+ * ~~~
+ * this->item->name
+ * ~~~
+ *
+ * The most likely place where you will encounter expressions is in the context
+ * of list models and list widgets using them. For example, Gtk::DropDown is
+ * evaluating a %Gtk::Expression to obtain strings from the items in its model
+ * that it can then use to match against the contents of its search entry.
+ * Gtk::StringFilter is using a %Gtk::Expression for similar reasons.
+ *
+ * By default, expressions are not paying attention to changes and evaluation is
+ * just a snapshot of the current state at a given time. To get informed about
+ * changes, an expression needs to be "watched" via a Gtk::ExpressionWatch, which
+ * will cause a callback to be called whenever the value of the expression may
+ * have changed. watch() starts watching an expression, and
+ * Gtk::ExpressionWatch::unwatch() stops.
+ *
+ * Watches can be created for automatically updating the propery of an object,
+ * similar to the Glib::Binding mechanism, by using bind().
+ *
+ * @newin{3,98}
+ */
+template<class T>
+class Expression : public ExpressionBase
+{
+public:
+ using ValueType = T;
+
+ /** Evaluates the given expression and on success returns the result.
+ *
+ * It is possible that expressions cannot be evaluated - for example
+ * when the expression references objects that have been destroyed or
+ * set to <tt>nullptr</tt>. In that case the returned std::optional
+ * will not contain a value.
+ *
+ * @param this_ The this argument for the evaluation.
+ * @return The optional result of the evaluation.
+ */
+ std::optional<T> evaluate(const Glib::RefPtr<Glib::ObjectBase>& this_);
+
+ /** Installs a watch for the expression that calls the @a notify function
+ * whenever the evaluation of the expression may have changed.
+ *
+ * GTK cannot guarantee that the evaluation did indeed change when the @a notify
+ * gets invoked, but it guarantees the opposite: When it did in fact change,
+ * the @a notify will be invoked.
+ *
+ * @param this_ The `this` argument to watch.
+ * @param notify Callback to invoke when the expression changes.
+ * @return The newly installed watch.
+ */
+ Glib::RefPtr<ExpressionWatch<T>> watch(const Glib::RefPtr<Glib::ObjectBase>& this_,
+ const SlotNotify& notify);
+
+ /** Bind a target's @a property to the expression.
+ *
+ * The value that the expression evaluates to is set on the target.
+ * This is repeated whenever the expression changes to ensure that
+ * the object's property stays synchronized with the expression.
+ *
+ * If the expression's evaluation fails, target's @a property is not updated.
+ * You can ensure that this doesn't happen by using a fallback
+ * expression.
+ *
+ * @param property Property on the target to bind to.
+ * @param this_ The this argument for the evaluation of the expression.
+ * @return A Gtk::ExpressionWatch.
+ */
+ template<class T2>
+ Glib::RefPtr<ExpressionWatch<T>> bind(const Glib::PropertyProxy<T2>& property,
+ const Glib::RefPtr<Glib::ObjectBase>& this_ = nullptr);
+
+ /** @copydoc bind(const Glib::PropertyProxy<T2>&,const Glib::RefPtr<Glib::ObjectBase>&)
+ */
+ template<class T2>
+ Glib::RefPtr<ExpressionWatch<T>> bind(const Glib::PropertyProxy_WriteOnly<T2>& property,
+ const Glib::RefPtr<Glib::ObjectBase>& this_ = nullptr);
+};
+
+template<class T>
+class PropertyExpression final : public Expression<T>
+{
+public:
+ /** Get the GType for this class, for use with the underlying GObject type system.
+ */
+ static GType get_type() G_GNUC_CONST;
+
+ /** Creates an expression that looks up a property via the
+ * `this` argument.
+ *
+ * If the resulting object conforms to @a this_type, its property
+ * named @a property_name will be queried.
+ * Otherwise, this expression's evaluation will fail.
+ *
+ * The given @a this_type must have a property with @a property_name.
+ *
+ * @param this_type The type to expect for the this type.
+ * @param property_name Name of the property.
+ * @return A new Gtk::Expression.
+ */
+ static Glib::RefPtr<PropertyExpression> create(GType this_type,
+ const Glib::ustring& property_name);
+
+ /** Creates an expression that looks up a property via the
+ * given @a expression.
+ *
+ * If the resulting object conforms to @a OT, its property
+ * named @a property_name will be queried.
+ * Otherwise, this expression's evaluation will fail.
+ *
+ * The value type of the given @a expression must have a property with
+ * @a property_name.
+ *
+ * @param expression Expression to evaluate to get the object to query.
+ * @param property_name Name of the property.
+ * @return A new Gtk::Expression.
+ */
+ template<class OT>
+ static Glib::RefPtr<PropertyExpression> create(const Glib::RefPtr<OT>& expression,
+ const Glib::ustring& property_name);
+
+ /** Creates an expression that looks up a property via the
+ * given @a expression.
+ *
+ * If the resulting object conforms to @a this_type, its property
+ * named @a property_name will be queried.
+ * Otherwise, this expression's evaluation will fail.
+ *
+ * The given @a this_type must have a property with @a property_name.
+ *
+ * @param this_type The type to expect for the this type.
+ * @param expression Expression to evaluate to get the object to query.
+ * @param property_name Name of the property.
+ * @return A new Gtk::Expression.
+ */
+ template<class OT>
+ static Glib::RefPtr<PropertyExpression> create(GType this_type,
+ const Glib::RefPtr<OT>& expression,
+ const Glib::ustring& property_name);
+};
+
+template<class T>
+class ConstantExpression final : public Expression<T>
+{
+public:
+ /** Get the GType for this class, for use with the underlying GObject type system.
+ */
+ static GType get_type() G_GNUC_CONST;
+
+ /** Creates a Gtk::Expression that evaluates to the object given by
+ * the arguments.
+ *
+ * @param args Arguments to create the object from.
+ * @return A new Gtk::Expression.
+ */
+ template<class... T_Args>
+ static Glib::RefPtr<ConstantExpression<T>> create(T_Args&&... args);
+
+ /** Creates an expression that always evaluates to the given @a value.
+ *
+ * @param value A Value.
+ * @return A new Gtk::Expression.
+ */
+ static Glib::RefPtr<ConstantExpression<T>> create_for_value(const Glib::Value<T>& value);
+};
+
+template<class T>
+class ObjectExpression final : public Expression<T>
+{
+public:
+ /** Get the GType for this class, for use with the underlying GObject type system.
+ */
+ static GType get_type() G_GNUC_CONST;
+
+ /** Creates an expression evaluating to the given @a object with a weak reference.
+ * Once the @a object is disposed, it will fail to evaluate.
+ * This expression is meant to break reference cycles.
+ *
+ * If you want to keep a reference to @a object, use Gtk::ConstantExpression::create().
+ *
+ * @param object %Object to watch.
+ * @return A new Gtk::Expression.
+ */
+ static Glib::RefPtr<ObjectExpression<T>> create(const T& object);
+};
+
+template<class T>
+class ClosureExpression final : public Expression<T>
+{
+public:
+ /** Get the GType for this class, for use with the underlying GObject type system.
+ */
+ static GType get_type() G_GNUC_CONST;
+
+ /** Creates a Gtk::Expression that calls @a slot when it is evaluated.
+ * @a slot is called with the @a this object and the results of evaluating
+ * the @a params expressions.
+ *
+ * Example:
+ * ~~~
+ * Glib::ustring get_string(Glib::RefPtr<Glib::ObjectBase> this_, double a, int b)
+ * {
+ * return Glib::ustring::sprintf("a is %f, b is %d", a, b);
+ * }
+ *
+ * Glib::RefPtr<Gtk::Expression<double>> expr1 = ...
+ * Glib::RefPtr<Gtk::Expression<int>> expr2 = ...
+ *
+ * Glib::RefPtr<Gtk::Expression<Glib::ustring>> expr3 =
+ * Gtk::ClosureExpression<Glib::ustring>::create(
+ * sigc::ptr_fun(get_string), expr1, expr2);
+ * ~~~
+ *
+ * @param slot Slot to call when evaluating this expression.
+ * @param params Expressions for each parameter.
+ * @return A new Gtk::Expression.
+ */
+ template<class S, class... ATs>
+ static Glib::RefPtr<ClosureExpression<T>> create(S slot, const Glib::RefPtr<ATs>&... params);
+
+private:
+ template<class A1, class... ATs>
+ static void fill_params(GtkExpression** gparams, const Glib::RefPtr<A1>& a1,
+ const Glib::RefPtr<ATs>&... eas);
+
+ static void fill_params(GtkExpression** gparams);
+};
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+namespace Expression_Private
+{
+
+void watch_callback(gpointer data);
+
+template<class T, class... ATs>
+class Invoker
+{
+public:
+ explicit Invoker(const sigc::slot<T(ATs...)>& slot);
+ void invoke(const GValue* param_values, GValue* return_value);
+
+private:
+ sigc::slot<T(ATs...)> the_slot;
+
+ template<class PT>
+ PT eval_param(const GValue* gv);
+
+ template<std::size_t... I>
+ void invoke(const GValue* param_values, GValue* return_value,
+ std::index_sequence<I...>);
+};
+
+
+template<class TI>
+void closure_marshal(GClosure* closure,
+ GValue* return_value,
+ guint n_param_values,
+ const GValue* param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data);
+
+void closure_callback_func();
+
+template<class TI>
+void closure_destroy(gpointer data, GClosure* closure);
+
+} // namespace Expression_Private
+
+template<class T>
+std::optional<T> Expression<T>::evaluate(const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+ Glib::Value<T> value;
+ bool result = gtk_expression_evaluate(gobj(), this_ ? this_->gobj() : nullptr, value.gobj());
+ if (!result)
+ return {};
+
+ if (!G_VALUE_HOLDS(value.gobj(), Glib::Value<T>::value_type()))
+ {
+ g_warning("%s: The evaluated expression has type '%s', expected '%s'", G_STRLOC,
+ g_type_name(G_VALUE_TYPE(value.gobj())), g_type_name(Glib::Value<T>::value_type()));
+ return {};
+ }
+
+ return value.get();
+}
+
+template<class T>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::watch(const Glib::RefPtr<Glib::ObjectBase>& this_,
+ const SlotNotify& notify)
+{
+ auto slot_copy = new SlotNotify(notify);
+ return Glib::wrap<T>(gtk_expression_watch(gobj(), this_ ? this_->gobj() : nullptr,
+ &Expression_Private::watch_callback, slot_copy,
+ &Glib::destroy_notify_delete<SlotNotify>), true);
+}
+
+template<class T>
+template<class T2>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::bind(const Glib::PropertyProxy<T2>& property,
+ const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+ return Glib::wrap<T>(gtk_expression_bind(gobj_copy(), property.get_object()->gobj(),
+ property.get_name(),
+ this_ ? this_->gobj() : nullptr), true);
+}
+
+template<class T>
+template<class T2>
+Glib::RefPtr<ExpressionWatch<T>> Expression<T>::bind(
+ const Glib::PropertyProxy_WriteOnly<T2>& property,
+ const Glib::RefPtr<Glib::ObjectBase>& this_)
+{
+ return Glib::wrap<T>(gtk_expression_bind(gobj_copy(), property.get_object()->gobj(),
+ property.get_name(),
+ this_ ? this_->gobj() : nullptr), true);
+}
+
+template<class T>
+GType PropertyExpression<T>::get_type()
+{
+ return gtk_property_expression_get_type();
+}
+
+template<class T>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+ GType this_type, const Glib::ustring& property_name)
+{
+ return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+ reinterpret_cast<Gtk::PropertyExpression<T>*>(
+ gtk_property_expression_new(this_type,
+ nullptr,
+ property_name.c_str())));
+}
+
+template<class T>
+template<class OT>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+ const Glib::RefPtr<OT>& expression, const Glib::ustring& property_name)
+{
+ return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+ reinterpret_cast<Gtk::PropertyExpression<T>*>(
+ gtk_property_expression_new(Glib::Value<typename OT::ValueType>::value_type(),
+ expression->gobj_copy(),
+ property_name.c_str())));
+}
+
+template<class T>
+template<class OT>
+Glib::RefPtr<PropertyExpression<T>> PropertyExpression<T>::create(
+ GType this_type, const Glib::RefPtr<OT>& expression, const Glib::ustring& property_name)
+{
+ return Glib::make_refptr_for_instance<Gtk::PropertyExpression<T>>(
+ reinterpret_cast<Gtk::PropertyExpression<T>*>(
+ gtk_property_expression_new(this_type,
+ expression->gobj_copy(),
+ property_name.c_str())));
+}
+
+template<class T>
+GType ConstantExpression<T>::get_type()
+{
+ return gtk_constant_expression_get_type();
+}
+
+template<class T>
+template<class... T_Args>
+Glib::RefPtr<ConstantExpression<T>> ConstantExpression<T>::create(T_Args&&... args)
+{
+ Glib::Value<T> value;
+ value.init(Glib::Value<T>::value_type());
+ value.set(T(std::forward<T_Args>(args)...));
+ return create_for_value(value);
+}
+
+template<class T>
+Glib::RefPtr<ConstantExpression<T>> ConstantExpression<T>::create_for_value(
+ const Glib::Value<T>& value)
+{
+ return Glib::make_refptr_for_instance<Gtk::ConstantExpression<T>>(
+ reinterpret_cast<Gtk::ConstantExpression<T>*>(
+ gtk_constant_expression_new_for_value(value.gobj())));
+}
+
+template<class T>
+GType ObjectExpression<T>::get_type()
+{
+ return gtk_object_expression_get_type();
+}
+
+template<class T>
+Glib::RefPtr<ObjectExpression<T>> ObjectExpression<T>::create(const T& object)
+{
+ return Glib::make_refptr_for_instance<Gtk::ObjectExpression<T>>(
+ reinterpret_cast<Gtk::ObjectExpression<T>*>(
+ gtk_object_expression_new(G_OBJECT(object->gobj()))));
+}
+
+template<class T>
+GType ClosureExpression<T>::get_type()
+{
+ return gtk_cclosure_expression_get_type();
+}
+
+template<class T>
+template<class S, class... ATs>
+Glib::RefPtr<ClosureExpression<T>> ClosureExpression<T>::create(S slot,
+ const Glib::RefPtr<ATs>&... params)
+{
+ using TI = Expression_Private::Invoker<T, Glib::RefPtr<Glib::ObjectBase>,
+ typename ATs::ValueType...>;
+ auto invoker = new TI(slot);
+ guint n_params = sizeof...(params);
+ auto gparams = new GtkExpression*[n_params];
+ fill_params(gparams, params...);
+
+ auto object = gtk_cclosure_expression_new(
+ Glib::Value<T>::value_type(),
+ Expression_Private::closure_marshal<TI>,
+ n_params,
+ gparams,
+ Expression_Private::closure_callback_func,
+ invoker,
+ Expression_Private::closure_destroy<TI>);
+ delete[] gparams;
+
+ return Glib::make_refptr_for_instance<Gtk::ClosureExpression<T>>(
+ reinterpret_cast<Gtk::ClosureExpression<T>*>(object));
+}
+
+template<class T>
+template<class A1, class... ATs>
+void ClosureExpression<T>::fill_params(GtkExpression** gparams, const Glib::RefPtr<A1>& a1,
+ const Glib::RefPtr<ATs>&... eas)
+{
+ gparams[0] = a1->gobj_copy();
+ fill_params(gparams + 1, eas...);
+}
+
+template<class T>
+void ClosureExpression<T>::fill_params(GtkExpression**)
+{}
+
+
+namespace Expression_Private
+{
+
+template<class T, class... ATs>
+Invoker<T, ATs...>::Invoker(const sigc::slot<T(ATs...)>& slot)
+:
+ the_slot(slot)
+{}
+
+template<class T, class... ATs>
+template<class PT>
+PT Invoker<T, ATs...>::eval_param(const GValue* gv)
+{
+ Glib::Value<PT> v;
+ v.init(gv);
+ return v.get();
+}
+
+template<class T, class... ATs>
+template<std::size_t... I>
+void Invoker<T, ATs...>::invoke(const GValue* param_values, GValue* return_value,
+ std::index_sequence<I...>)
+{
+ T res = the_slot(eval_param<ATs>(¶m_values[I])...);
+ Glib::Value<T> rv;
+ rv.init(Glib::Value<T>::value_type());
+ rv.set(res);
+ g_value_copy(rv.gobj(), return_value);
+}
+
+template<class T, class... ATs>
+void Invoker<T, ATs...>::invoke(const GValue* param_values, GValue* return_value)
+{
+ invoke(param_values, return_value, std::make_index_sequence<sizeof...(ATs)>());
+}
+
+
+template<class TI>
+void closure_marshal(GClosure* closure,
+ GValue* return_value,
+ guint,
+ const GValue* param_values,
+ gpointer,
+ gpointer)
+{
+ static_cast<TI*>(closure->data)->invoke(param_values, return_value);
+}
+
+
+template<class TI>
+void closure_destroy(gpointer, GClosure* closure)
+{
+ delete static_cast<TI*>(closure->data);
+}
+
+} // namespace Expression_Private
+
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
+} // namespace Gtk
+
+
+namespace Glib
+{
+
+template<class T>
+RefPtr<Gtk::Expression<T>> wrap(GtkExpression* object, bool take_copy = false)
+{
+ if (take_copy && object)
+ gtk_expression_ref(object);
+
+ return Glib::make_refptr_for_instance<Gtk::Expression<T>>(
+ reinterpret_cast<Gtk::Expression<T>*>(object));
+}
+
+template<class T>
+class Value<RefPtr<Gtk::Expression<T>>> : public ValueBase
+{
+public:
+ using CppType = RefPtr<Gtk::Expression<T>>;
+
+ static GType value_type();
+
+ void set(const CppType& data);
+ CppType get() const;
+};
+
+template<class T>
+GType Value<RefPtr<Gtk::Expression<T>>>::value_type()
+{
+ return gtk_expression_get_type();
+}
+
+template<class T>
+void Value<RefPtr<Gtk::Expression<T>>>::set(const CppType& data)
+{
+ gtk_value_set_expression(&gobject_, const_cast<GtkExpression*>(data->gobj()));
+}
+
+template<class T>
+typename Value<RefPtr<Gtk::Expression<T>>>::CppType Value<RefPtr<Gtk::Expression<T>>>::get() const
+{
+ return wrap<T>(gtk_value_get_expression(&gobject_), true);
+}
+
+} // namespace Glib
diff --git a/gtk/src/expressionwatch.ccg b/gtk/src/expressionwatch.ccg
new file mode 100644
index 00000000..57b87fdc
--- /dev/null
+++ b/gtk/src/expressionwatch.ccg
@@ -0,0 +1,17 @@
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
diff --git a/gtk/src/expressionwatch.hg b/gtk/src/expressionwatch.hg
new file mode 100644
index 00000000..78308d22
--- /dev/null
+++ b/gtk/src/expressionwatch.hg
@@ -0,0 +1,82 @@
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+_CONFIGINCLUDE(gtkmmconfig.h)
+
+#include <glibmm/object.h>
+#include <gtk/gtk.h>
+
+_DEFS(gtkmm,gtk)
+
+namespace Gtk
+{
+class GTKMM_API ExpressionWatchBase
+{
+ _CLASS_OPAQUE_REFCOUNTED(ExpressionWatchBase, GtkExpressionWatch, NONE, gtk_expression_watch_ref,
gtk_expression_watch_unref, GTKMM_API)
+ _IGNORE(gtk_expression_watch_ref, gtk_expression_watch_unref, gtk_expression_watch_evaluate)
+
+public:
+ _WRAP_METHOD(void unwatch(), gtk_expression_watch_unwatch)
+};
+
+template<class T>
+class GTKMM_API ExpressionWatch final : public ExpressionWatchBase
+{
+public:
+ /** Evaluates the watched expression and on success returns the result.
+ *
+ * This is equivalent to calling Gtk::Expression::evaluate() with the
+ * expression and this pointer originally used to create the watch.
+ *
+ * @return The optional result of the evaluation.
+ */
+ std::optional<T> evaluate();
+};
+
+template<class T>
+std::optional<T> ExpressionWatch<T>::evaluate()
+{
+ Glib::Value<T> value;
+ bool result = gtk_expression_watch_evaluate(gobj(), value.gobj());
+ if (!result)
+ return {};
+
+ if (!G_VALUE_HOLDS(value.gobj(), Glib::Value<T>::value_type()))
+ {
+ g_warning("%s: The evaluated expression has type '%s', expected '%s'", G_STRLOC,
+ g_type_name(G_VALUE_TYPE(value.gobj())), g_type_name(Glib::Value<T>::value_type()));
+ return {};
+ }
+
+ return value.get();
+}
+
+} // namespace Gtk
+
+namespace Glib
+{
+
+template<class T>
+RefPtr<Gtk::ExpressionWatch<T>> wrap(GtkExpressionWatch* object, bool take_copy = false)
+{
+ if (take_copy && object)
+ gtk_expression_watch_ref(object);
+
+ return Glib::make_refptr_for_instance<Gtk::ExpressionWatch<T>>(
+ reinterpret_cast<Gtk::ExpressionWatch<T>*>(object));
+}
+
+} // namespace Glib
diff --git a/gtk/src/filelist.am b/gtk/src/filelist.am
index 816a091c..400b3647 100644
--- a/gtk/src/filelist.am
+++ b/gtk/src/filelist.am
@@ -89,6 +89,8 @@ gtkmm_files_any_hg = \
eventcontrollermotion.hg \
eventcontrollerscroll.hg \
expander.hg \
+ expression.hg \
+ expressionwatch.hg \
filechooser.hg \
filechooserbutton.hg \
filechooserdialog.hg \
diff --git a/gtk/src/gtk_docs_override.xml b/gtk/src/gtk_docs_override.xml
index e5b7fe53..e41a606c 100644
--- a/gtk/src/gtk_docs_override.xml
+++ b/gtk/src/gtk_docs_override.xml
@@ -1414,6 +1414,38 @@ constructed by the Gtk::Builder instance.
</return>
</function>
+<function name="gtk_expression_get_value_type">
+<return>The type returned from Gtk::Expression::evaluate()
+</return>
+</function>
+
+<function name="gtk_expression_is_static">
+<description>Checks if the expression is static.
+
+A static expression will never change its result when
+Gtk::Expression::evaluate() is called on it with the same arguments.
+
+That means a call to Gtk::Expression::watch() is not necessary because
+it will never trigger a notify.
+</description>
+</function>
+
+<function name="gtk_expression_watch_unwatch">
+<description>
+Stops watching an expression that was established via Gtk::Expression::watch().
+</description>
+</function>
+
+<function name="gtk_expression_watch_evaluate">
+<description>
+Evaluates the watched expression and on success stores the result
+in @value.
+
+This is equivalent to calling Gtk::Expression::evaluate() with the
+expression and this pointer originally used to create @watch.
+</description>
+</function>
+
<!-- TODO: Remove this signal description when gtk's description contains -->
<!-- the correct number of parameters (no @keyval). -->
<signal name="GtkEventControllerKey::modifiers">
diff --git a/gtk/src/gtk_extra_objects.defs b/gtk/src/gtk_extra_objects.defs
index 4ac9dc95..05151ad2 100644
--- a/gtk/src/gtk_extra_objects.defs
+++ b/gtk/src/gtk_extra_objects.defs
@@ -138,6 +138,12 @@
(gtype-id "GTK_TYPE_DRAG_SOURCE")
)
+(define-object DropDown
+ (in-module "Gtk")
+ (c-name "GtkDropDown")
+ (gtype-id "GTK_TYPE_DROP_DOWN")
+)
+
(define-object DropTarget
(in-module "Gtk")
(c-name "GtkDropTarget")
diff --git a/gtk/src/stringfilter.hg b/gtk/src/stringfilter.hg
index 53493bd8..90dc5700 100644
--- a/gtk/src/stringfilter.hg
+++ b/gtk/src/stringfilter.hg
@@ -15,6 +15,7 @@
*/
#include <gtkmm/filter.h>
+#include <gtkmm/expression.h>
_DEFS(gtkmm,gtk)
_PINCLUDE(gtkmm/private/filter_p.h)
@@ -39,24 +40,23 @@ class GTKMM_API StringFilter : public Filter
_STRUCT_NOT_HIDDEN
protected:
- //TODO: change this when GtkExpression has been wrapped
- _WRAP_CTOR(StringFilter(GtkExpression* expression), gtk_string_filter_new)
+ _WRAP_CTOR(StringFilter(const Glib::RefPtr<Expression<Glib::ustring>>& expression), gtk_string_filter_new)
public:
_WRAP_ENUM(MatchMode, GtkStringFilterMatchMode, decl_prefix GTKMM_API)
- _WRAP_CREATE(GtkExpression* expression)
+ _WRAP_CREATE(const Glib::RefPtr<Expression<Glib::ustring>>& expression)
_WRAP_METHOD(Glib::ustring get_search() const, gtk_string_filter_get_search)
_WRAP_METHOD(void set_search(const Glib::ustring& search), gtk_string_filter_set_search)
- _WRAP_METHOD(GtkExpression* get_expression() const, gtk_string_filter_get_expression)
- _WRAP_METHOD(void set_expression(GtkExpression* expression), gtk_string_filter_set_expression)
+ _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const,
gtk_string_filter_get_expression)
+ _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression),
gtk_string_filter_set_expression)
_WRAP_METHOD(bool get_ignore_case() const, gtk_string_filter_get_ignore_case)
_WRAP_METHOD(void set_ignore_case(bool ignore_case = true), gtk_string_filter_set_ignore_case)
_WRAP_METHOD(MatchMode get_match_mode() const, gtk_string_filter_get_match_mode)
_WRAP_METHOD(void set_match_mode(MatchMode mode), gtk_string_filter_set_match_mode)
- _WRAP_PROPERTY("expression", GtkExpression*)
+ _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
_WRAP_PROPERTY("ignore-case", bool)
_WRAP_PROPERTY("match-mode", MatchMode)
_WRAP_PROPERTY("search", Glib::ustring)
diff --git a/gtk/src/stringsorter.hg b/gtk/src/stringsorter.hg
index 13254d0b..c3e5ee9c 100644
--- a/gtk/src/stringsorter.hg
+++ b/gtk/src/stringsorter.hg
@@ -15,6 +15,7 @@
*/
#include <gtkmm/sorter.h>
+#include <gtkmm/expression.h>
_DEFS(gtkmm,gtk)
_PINCLUDE(gtkmm/private/sorter_p.h)
@@ -22,7 +23,7 @@ _PINCLUDE(gtkmm/private/sorter_p.h)
namespace Gtk
{
-/** Sort by comparing strings
+/** Sort by comparing strings.
*
* %Gtk::StringSorter is a Gtk::Sorter that compares strings. It does the
* comparison in a linguistically correct way using the current locale by
@@ -40,18 +41,17 @@ class GTKMM_API StringSorter : public Sorter
_STRUCT_NOT_HIDDEN
protected:
- //TODO: change this when GtkExpression has been wrapped
- _WRAP_CTOR(StringSorter(GtkExpression* expression), gtk_string_sorter_new)
+ _WRAP_CTOR(StringSorter(const Glib::RefPtr<Expression<Glib::ustring>>& expression), gtk_string_sorter_new)
public:
- _WRAP_CREATE(GtkExpression* expression)
+ _WRAP_CREATE(const Glib::RefPtr<Expression<Glib::ustring>>& expression)
- _WRAP_METHOD(GtkExpression* get_expression() const, gtk_string_sorter_get_expression)
- _WRAP_METHOD(void set_expression(GtkExpression* expression), gtk_string_sorter_set_expression)
+ _WRAP_METHOD(Glib::RefPtr<Expression<Glib::ustring>> get_expression() const,
gtk_string_sorter_get_expression)
+ _WRAP_METHOD(void set_expression(const Glib::RefPtr<Expression<Glib::ustring>>& expression),
gtk_string_sorter_set_expression)
_WRAP_METHOD(bool get_ignore_case() const, gtk_string_sorter_get_ignore_case)
_WRAP_METHOD(void set_ignore_case(bool ignore_case = true), gtk_string_sorter_set_ignore_case)
- _WRAP_PROPERTY("expression", GtkExpression*)
+ _WRAP_PROPERTY("expression", Glib::RefPtr<Expression<Glib::ustring>>)
_WRAP_PROPERTY("ignore-case", bool)
};
diff --git a/tools/m4/convert_gtk.m4 b/tools/m4/convert_gtk.m4
index b1e9a4e8..7dccd88c 100644
--- a/tools/m4/convert_gtk.m4
+++ b/tools/m4/convert_gtk.m4
@@ -604,3 +604,6 @@ _CONVERSION(`GtkSorter*',`Glib::RefPtr<Sorter>',`Glib::wrap($3)')
_CONVERSION(`GtkTreeListRow*',`Glib::RefPtr<TreeListRow>',`Glib::wrap($3)')
_CONVERSION(`const Glib::RefPtr<Filter>&', `GtkFilter*', __CONVERT_REFPTR_TO_P)
_CONVERSION(`GtkFilter*',`Glib::RefPtr<Filter>',`Glib::wrap($3)')
+
+_CONVERSION(`const Glib::RefPtr<Expression<Glib::ustring>>&',`GtkExpression*',`(($3) ? ($3)->gobj() :
nullptr)')
+_CONVERSION(`GtkExpression*',`Glib::RefPtr<Expression<Glib::ustring>>',`Glib::wrap<Glib::ustring>($3)')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]