[gtk/wip/chergert/action-parent: 1/4] widget: add gtk_widget_set_action_parent()




commit 3b3ad3d6cc8b34dd67f0431b16c1cb5a63135951
Author: Christian Hergert <chergert redhat com>
Date:   Mon May 2 14:36:26 2022 -0700

    widget: add gtk_widget_set_action_parent()
    
    This adds a new function to the 4.8 ABI that allows setting an action
    parent for a widget. The action parent affects the action muxing so that
    instead of using the widgets direct ancestor, an alternate widget's
    action muxer may be used.
    
    You may not set an action parent for a widget that is a direct descendant
    of your widget as that would cause cycles in action resolution.
    
    You might find this API useful for situations where you want menus in
    headerbars to route through action muxers for the current document as
    well as toolbars or sidebars.
    
    Fixes #4860

 gtk/gtkactionmuxer.c   | 18 +++++++++++++
 gtk/gtkwidget.c        | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--
 gtk/gtkwidget.h        |  3 +++
 gtk/gtkwidgetprivate.h |  1 +
 4 files changed, 89 insertions(+), 2 deletions(-)
---
diff --git a/gtk/gtkactionmuxer.c b/gtk/gtkactionmuxer.c
index 06660ddf89..ce7d66889e 100644
--- a/gtk/gtkactionmuxer.c
+++ b/gtk/gtkactionmuxer.c
@@ -1356,6 +1356,23 @@ gtk_action_muxer_get_parent (GtkActionMuxer *muxer)
   return muxer->parent;
 }
 
+static gboolean
+muxer_will_cycle (GtkActionMuxer *muxer,
+                  GtkActionMuxer *parent)
+{
+  GtkActionMuxer *ancestor;
+
+  for (ancestor = parent;
+       ancestor != NULL;
+       ancestor = gtk_action_muxer_get_parent (ancestor))
+    {
+      if (ancestor == muxer)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 /*< private >
  * gtk_action_muxer_set_parent:
  * @muxer: a `GtkActionMuxer`
@@ -1369,6 +1386,7 @@ gtk_action_muxer_set_parent (GtkActionMuxer *muxer,
 {
   g_return_if_fail (GTK_IS_ACTION_MUXER (muxer));
   g_return_if_fail (parent == NULL || GTK_IS_ACTION_MUXER (parent));
+  g_return_if_fail (parent == NULL || !muxer_will_cycle (muxer, parent));
 
   if (muxer->parent == parent)
     return;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index afe7e9224e..f3c51db268 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -7390,6 +7390,8 @@ gtk_widget_dispose (GObject *object)
   GSList *sizegroups;
   GtkATContext *at_context;
 
+  g_clear_object (&priv->action_parent);
+
   if (priv->muxer != NULL)
     g_object_run_dispose (G_OBJECT (priv->muxer));
 
@@ -10808,13 +10810,20 @@ void
 _gtk_widget_update_parent_muxer (GtkWidget *widget)
 {
   GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+  GtkActionMuxer *parent_muxer;
   GtkWidget *child;
 
   if (priv->muxer == NULL)
     return;
 
-  gtk_action_muxer_set_parent (priv->muxer,
-                               gtk_widget_get_parent_muxer (widget, FALSE));
+  if (priv->action_parent != NULL &&
+      !gtk_widget_is_ancestor (priv->action_parent, widget))
+    parent_muxer = _gtk_widget_get_action_muxer (priv->action_parent, FALSE);
+  else
+    parent_muxer = gtk_widget_get_parent_muxer (widget, FALSE);
+
+  gtk_action_muxer_set_parent (priv->muxer, parent_muxer);
+
   for (child = gtk_widget_get_first_child (widget);
        child != NULL;
        child = gtk_widget_get_next_sibling (child))
@@ -12934,3 +12943,59 @@ gtk_widget_set_active_state (GtkWidget *widget,
         gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_ACTIVE);
     }
 }
+
+/**
+ * gtk_widget_set_action_parent:
+ * @widget: a [class@Gtk.Widget]
+ * @action_parent: (nullable): a [class@Gtk.Widget]
+ *
+ * Sets the action parent for @widget.
+ *
+ * Actions will resolve through @action_parent for @widget and all of
+ * it's descendants unless otherwise specified with
+ * [method@Gtk.Widget.set_action_parent].
+ *
+ * Setting an action parent can be useful when you want actions within
+ * a menu or toolbar to resolve through a document widget.
+ *
+ * To unset an action parent, use `NULL` for @action_parent and the widget
+ * will resume using the parent widget as the action parent.
+ *
+ * It is a programming error to set an action parent which will cause a
+ * cycle to occur.
+ *
+ * Since: 4.8
+ */
+void
+gtk_widget_set_action_parent (GtkWidget *widget,
+                              GtkWidget *action_parent)
+{
+  GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+  GtkActionMuxer *muxer;
+  GtkActionMuxer *parent_muxer;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (action_parent != widget);
+  g_return_if_fail (!action_parent || GTK_IS_WIDGET (action_parent));
+  g_return_if_fail (!action_parent || !gtk_widget_is_ancestor (action_parent, widget));
+
+  muxer = _gtk_widget_get_action_muxer (widget, FALSE);
+
+  if (action_parent == NULL)
+    {
+      if (muxer != NULL)
+        {
+          parent_muxer = gtk_widget_get_parent_muxer (widget, FALSE);
+          gtk_action_muxer_set_parent (muxer, parent_muxer);
+        }
+    }
+  else
+    {
+      if (muxer == NULL)
+        muxer = _gtk_widget_get_action_muxer (widget, TRUE);
+      parent_muxer = _gtk_widget_get_action_muxer (action_parent, TRUE);
+      gtk_action_muxer_set_parent (muxer, parent_muxer);
+    }
+
+  g_set_object (&priv->action_parent, action_parent);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 0788fcdd81..1a9793f296 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -924,6 +924,9 @@ char **                 gtk_widget_get_css_classes      (GtkWidget   *widget);
 GDK_AVAILABLE_IN_ALL
 void                    gtk_widget_set_css_classes      (GtkWidget   *widget,
                                                          const char **classes);
+GDK_AVAILABLE_IN_4_8
+void                    gtk_widget_set_action_parent    (GtkWidget   *widget,
+                                                         GtkWidget   *action_parent);
 
 
 
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index e1e336e6e6..b8f17c8f23 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -185,6 +185,7 @@ struct _GtkWidgetPrivate
   GtkListListModel *children_observer;
   GtkListListModel *controller_observer;
   GtkActionMuxer *muxer;
+  GtkWidget *action_parent;
 
   GtkWidget *focus_child;
 


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