[gtk/wip/matthiasc/popup4: 2/106] widget: Add surface relative transform changed callback
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/matthiasc/popup4: 2/106] widget: Add surface relative transform changed callback
- Date: Wed, 24 Apr 2019 11:37:26 +0000 (UTC)
commit 08cf053a9c88e886f54b11ae698fdef3527fe0e2
Author: Jonas Ådahl <jadahl gmail com>
Date: Thu Apr 18 18:55:13 2019 +0200
widget: Add surface relative transform changed callback
Added two new private GtkWidget API:
* gtk_widget_add_surface_transform_changed_callback()
* gtk_widget_remove_surface_transform_changed_callback()
The intention is to let the user know when a widget transform relative
to the surface changes. It works by calculating the surface relative
transform during allocation, and notifying the callbacks if it changed
since last time. Each widget adds itself as a listener to its parent
widget, thus will be triggered if a parents surface relative transform
changes.
gtk/gtkwidget.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++
gtk/gtkwidgetprivate.h | 21 ++++
meson.build | 2 +-
3 files changed, 278 insertions(+), 1 deletion(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index ed93a7ba20..ab33790cb2 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -713,6 +713,9 @@ static void gtk_widget_update_input_shape (GtkWidget *widget);
static gboolean gtk_widget_class_get_visible_by_default (GtkWidgetClass *widget_class);
+static void remove_parent_surface_transform_changed_listener (GtkWidget *widget);
+static void add_parent_surface_transform_changed_listener (GtkWidget *widget);
+
/* --- variables --- */
static gint GtkWidget_private_offset = 0;
@@ -2905,6 +2908,9 @@ gtk_widget_root (GtkWidget *widget)
if (priv->context)
gtk_style_context_set_display (priv->context, gtk_root_get_display (priv->root));
+ if (priv->surface_transform_changed_callbacks)
+ add_parent_surface_transform_changed_listener (widget);
+
GTK_WIDGET_GET_CLASS (widget)->root (widget);
g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_ROOT]);
@@ -2922,6 +2928,9 @@ gtk_widget_unroot (GtkWidget *widget)
g_assert (priv->root);
g_assert (!priv->realized);
+ if (priv->parent_surface_transform_changed_parent)
+ remove_parent_surface_transform_changed_listener (widget);
+
GTK_WIDGET_GET_CLASS (widget)->unroot (widget);
if (priv->context)
@@ -3586,6 +3595,249 @@ gtk_widget_disconnect_frame_clock (GtkWidget *widget)
}
}
+typedef struct _GtkSurfaceTransformChangedCallbackInfo GtkSurfaceTransformChangedCallbackInfo;
+
+struct _GtkSurfaceTransformChangedCallbackInfo
+{
+ guint id;
+ GtkSurfaceTransformChangedCallback callback;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+static void
+surface_transform_changed_callback_info_destroy (GtkSurfaceTransformChangedCallbackInfo *info)
+{
+ if (info->notify)
+ info->notify (info->user_data);
+
+ g_slice_free (GtkSurfaceTransformChangedCallbackInfo, info);
+}
+
+static void
+notify_surface_transform_changed (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ graphene_matrix_t *surface_transform;
+ GList *l;
+
+ if (priv->cached_surface_transform_valid)
+ surface_transform = &priv->cached_surface_transform;
+ else
+ surface_transform = NULL;
+
+ for (l = priv->surface_transform_changed_callbacks; l;)
+ {
+ GtkSurfaceTransformChangedCallbackInfo *info = l->data;
+ GList *l_next = l->next;
+
+ if (info->callback (widget,
+ surface_transform,
+ info->user_data) == G_SOURCE_REMOVE)
+ {
+ priv->surface_transform_changed_callbacks =
+ g_list_delete_link (priv->surface_transform_changed_callbacks, l);
+ surface_transform_changed_callback_info_destroy (info);
+ }
+
+ l = l_next;
+ }
+}
+
+static void
+destroy_surface_transform_changed_callbacks (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ GList *l;
+
+ for (l = priv->surface_transform_changed_callbacks; l;)
+ {
+ GtkSurfaceTransformChangedCallbackInfo *info = l->data;
+ GList *l_next = l->next;
+
+ priv->surface_transform_changed_callbacks =
+ g_list_delete_link (priv->surface_transform_changed_callbacks, l);
+ surface_transform_changed_callback_info_destroy (info);
+
+ l = l_next;
+ }
+}
+
+static void
+sync_widget_surface_transform (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ gboolean was_valid;
+ graphene_matrix_t prev_transform;
+
+ was_valid = priv->cached_surface_transform_valid;
+ prev_transform = priv->cached_surface_transform;
+
+ if (GTK_IS_ROOT (widget))
+ {
+ gsk_transform_to_matrix (priv->transform,
+ &priv->cached_surface_transform);
+ priv->cached_surface_transform_valid = TRUE;
+ }
+ else if (!priv->root)
+ {
+ priv->cached_surface_transform_valid = FALSE;
+ }
+ else if (gtk_widget_compute_transform (widget, GTK_WIDGET (priv->root),
+ &priv->cached_surface_transform))
+ {
+ priv->cached_surface_transform_valid = TRUE;
+ }
+ else
+ {
+ g_warning ("Could not compute surface transform");
+ priv->cached_surface_transform_valid = FALSE;
+ }
+
+ if (was_valid != priv->cached_surface_transform_valid ||
+ (was_valid && priv->cached_surface_transform_valid &&
+ !graphene_matrix_equal (&priv->cached_surface_transform,
+ &prev_transform)))
+ notify_surface_transform_changed (widget);
+}
+
+static guint surface_transform_changed_callback_id;
+
+static gboolean
+parent_surface_transform_changed_cb (GtkWidget *parent,
+ const graphene_matrix_t *transform,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (user_data);
+
+ sync_widget_surface_transform (widget);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+remove_parent_surface_transform_changed_listener (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+
+ g_assert (priv->parent_surface_transform_changed_parent);
+
+ gtk_widget_remove_surface_transform_changed_callback (
+ priv->parent_surface_transform_changed_parent,
+ priv->parent_surface_transform_changed_id);
+ priv->parent_surface_transform_changed_id = 0;
+ g_clear_object (&priv->parent_surface_transform_changed_parent);
+}
+
+static void
+add_parent_surface_transform_changed_listener (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+ GtkWidget *parent;
+
+ g_assert (!priv->parent_surface_transform_changed_parent);
+
+ parent = priv->parent;
+ priv->parent_surface_transform_changed_id =
+ gtk_widget_add_surface_transform_changed_callback (
+ parent,
+ parent_surface_transform_changed_cb,
+ widget,
+ NULL);
+ priv->parent_surface_transform_changed_parent = g_object_ref (parent);
+}
+
+/**
+ * gtk_widget_add_surface_transform_changed_callback:
+ * @widget: a #GtkWidget
+ * @callback: a function to call when the surface transform changes
+ * @user_data: data to pass to @callback
+ * @notify: function to call to free @user_data when the callback is removed
+ *
+ * Invokes the callback whenever the surface relative transform of the widget
+ * changes.
+ *
+ * Returns: an id for the connection of this callback. Remove the callback by
+ * passing the id returned from this funcction to
+ * gtk_widget_remove_surface_transform_changed_callback()
+ */
+guint
+gtk_widget_add_surface_transform_changed_callback (GtkWidget *widget,
+ GtkSurfaceTransformChangedCallback callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GtkWidgetPrivate *priv;
+ GtkSurfaceTransformChangedCallbackInfo *info;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+ g_return_val_if_fail (callback, 0);
+
+ priv = gtk_widget_get_instance_private (widget);
+
+ if (priv->parent && !priv->parent_surface_transform_changed_id)
+ add_parent_surface_transform_changed_listener (widget);
+
+ if (!priv->surface_transform_changed_callbacks)
+ sync_widget_surface_transform (widget);
+
+ info = g_slice_new0 (GtkSurfaceTransformChangedCallbackInfo);
+
+ info->id = ++surface_transform_changed_callback_id;
+ info->callback = callback;
+ info->user_data = user_data;
+ info->notify = notify;
+
+ priv->surface_transform_changed_callbacks =
+ g_list_prepend (priv->surface_transform_changed_callbacks, info);
+
+ return info->id;
+}
+
+/**
+ * gtk_widget_remove_surface_transform_changed_callback:
+ * @widget: a #GtkWidget
+ * @id: an id returned by gtk_widget_add_surface_transform_changed_callback()
+ *
+ * Removes a surface transform changed callback previously registered with
+ * gtk_widget_add_surface_transform_changed_callback().
+ */
+void
+gtk_widget_remove_surface_transform_changed_callback (GtkWidget *widget,
+ guint id)
+{
+ GtkWidgetPrivate *priv;
+ GList *l;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (id);
+
+ priv = gtk_widget_get_instance_private (widget);
+
+ for (l = priv->surface_transform_changed_callbacks; l; l = l->next)
+ {
+ GtkSurfaceTransformChangedCallbackInfo *info = l->data;
+
+ if (info->id == id)
+ {
+ priv->surface_transform_changed_callbacks =
+ g_list_delete_link (priv->surface_transform_changed_callbacks, l);
+
+ surface_transform_changed_callback_info_destroy (info);
+ break;
+ }
+ }
+
+ if (!priv->surface_transform_changed_callbacks)
+ {
+ if (priv->parent_surface_transform_changed_parent)
+ remove_parent_surface_transform_changed_listener (widget);
+
+ g_signal_handler_disconnect (widget, priv->parent_changed_handler_id);
+ priv->parent_changed_handler_id = 0;
+ }
+}
+
/**
* gtk_widget_realize:
* @widget: a #GtkWidget
@@ -4200,6 +4452,9 @@ gtk_widget_allocate (GtkWidget *widget,
priv->transform = transform;
+ if (priv->surface_transform_changed_callbacks)
+ sync_widget_surface_transform (widget);
+
if (!alloc_needed && !size_changed && !baseline_changed)
{
/* Still have to move the window... */
@@ -8125,6 +8380,7 @@ gtk_widget_real_destroy (GtkWidget *object)
gtk_grab_remove (widget);
destroy_tick_callbacks (widget);
+ destroy_surface_transform_changed_callbacks (widget);
}
static void
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 7409ee047b..2e596f24c4 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -41,6 +41,10 @@
G_BEGIN_DECLS
+typedef gboolean (*GtkSurfaceTransformChangedCallback) (GtkWidget *widget,
+ const graphene_matrix_t *surface_transform,
+ gpointer user_data);
+
#define GTK_STATE_FLAGS_BITS 14
struct _GtkWidgetPrivate
@@ -116,6 +120,14 @@ struct _GtkWidgetPrivate
guint clock_tick_id;
GList *tick_callbacks;
+ /* Surface relative transform updates callbacks */
+ guint parent_surface_transform_changed_id;
+ GtkWidget *parent_surface_transform_changed_parent;
+ gulong parent_changed_handler_id;
+ GList *surface_transform_changed_callbacks;
+ gboolean cached_surface_transform_valid;
+ graphene_matrix_t cached_surface_transform;
+
/* The widget's name. If the widget does not have a name
* (the name is NULL), then its name (as returned by
* "gtk_widget_get_name") is its class's name.
@@ -344,6 +356,15 @@ gboolean gtk_widget_run_controllers (GtkWidget
GtkPropagationPhase phase);
+guint gtk_widget_add_surface_transform_changed_callback (GtkWidget
*widget,
+ GtkSurfaceTransformChangedCallback
callback,
+ gpointer
user_data,
+ GDestroyNotify
notify);
+
+void gtk_widget_remove_surface_transform_changed_callback (GtkWidget *widget,
+ guint id);
+
+
/* inline getters */
static inline GtkWidget *
diff --git a/meson.build b/meson.build
index 2060fea5a1..6e2b8eacd1 100644
--- a/meson.build
+++ b/meson.build
@@ -35,7 +35,7 @@ gdk_pixbuf_req = '>= 2.30.0'
introspection_req = '>= 1.39.0'
wayland_proto_req = '>= 1.12'
wayland_req = '>= 1.14.91'
-graphene_req = '>= 1.8.5'
+graphene_req = '>= 1.8.7'
epoxy_req = '>= 1.4'
cloudproviders_req = '>= 0.2.5'
xkbcommon_req = '>= 0.2.0'
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]