[libadwaita/wip/exalm/browsing-view: 15/18] header-bar: Magic back buttons




commit 4d85619387f5be9c76ca61d0db6d8911f0cf60a4
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Sun Oct 9 02:15:31 2022 +0400

    header-bar: Magic back buttons

 src/adw-header-bar.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/adw-header-bar.h |   6 ++
 2 files changed, 198 insertions(+), 3 deletions(-)
---
diff --git a/src/adw-header-bar.c b/src/adw-header-bar.c
index fb28d2ce..f2aeadae 100644
--- a/src/adw-header-bar.c
+++ b/src/adw-header-bar.c
@@ -23,6 +23,7 @@
 
 #include "adw-header-bar.h"
 
+#include "adw-button-content.h"
 #include "adw-browsing-view.h"
 #include "adw-enums.h"
 #include "adw-gizmo-private.h"
@@ -156,6 +157,7 @@ struct _AdwHeaderBar {
 
   guint show_start_title_buttons : 1;
   guint show_end_title_buttons : 1;
+  guint show_back_button : 1;
   guint track_default_decoration : 1;
 
   AdwCenteringPolicy centering_policy;
@@ -163,7 +165,11 @@ struct _AdwHeaderBar {
 
   GtkSizeGroup *size_group;
 
+  GtkWidget *back_button;
+  GtkWidget *browsing_view;
   GtkWidget *browsing_view_child;
+  AdwBrowsingViewChild *browsing_view_previous_child;
+  GBinding *back_button_binding;
 };
 
 enum {
@@ -171,6 +177,7 @@ enum {
   PROP_TITLE_WIDGET,
   PROP_SHOW_START_TITLE_BUTTONS,
   PROP_SHOW_END_TITLE_BUTTONS,
+  PROP_SHOW_BACK_BUTTON,
   PROP_DECORATION_LAYOUT,
   PROP_CENTERING_POLICY,
   LAST_PROP
@@ -213,6 +220,45 @@ create_end_window_controls (AdwHeaderBar *self)
   self->end_window_controls = controls;
 }
 
+static void
+update_back_button (AdwHeaderBar *self)
+{
+  AdwBrowsingViewChild *prev_child = NULL;
+
+  if (self->show_back_button && self->browsing_view && self->browsing_view_child)
+    prev_child = adw_browsing_view_get_previous_child (ADW_BROWSING_VIEW (self->browsing_view),
+                                                       ADW_BROWSING_VIEW_CHILD (self->browsing_view_child));
+
+  if (prev_child == self->browsing_view_previous_child)
+    return;
+
+  self->browsing_view_previous_child = prev_child;
+
+  if (prev_child) {
+    if (!self->back_button) {
+      self->back_button = gtk_button_new_from_icon_name ("go-previous-symbolic");
+
+      gtk_widget_add_css_class (self->back_button, "back-button");
+      gtk_actionable_set_action_name (GTK_ACTIONABLE (self->back_button), "browsing.pop");
+
+      gtk_box_insert_child_after (GTK_BOX (self->start_box),
+                                  self->back_button,
+                                  self->start_window_controls);
+    }
+
+    g_clear_pointer (&self->back_button_binding, g_binding_unbind);
+    self->back_button_binding =
+      g_object_bind_property (prev_child, "title",
+                              self->back_button, "tooltip-text",
+                              G_BINDING_SYNC_CREATE);
+  } else if (self->back_button) {
+    gtk_box_remove (GTK_BOX (self->start_box), self->back_button);
+    self->back_button = NULL;
+    self->browsing_view_previous_child = NULL;
+    self->back_button_binding = NULL;
+  }
+}
+
 static void
 update_title (AdwHeaderBar *self)
 {
@@ -262,21 +308,88 @@ construct_title_label (AdwHeaderBar *self)
   update_title (self);
 }
 
+static void
+browsing_view_pushed_cb (AdwHeaderBar *self)
+{
+  AdwBrowsingViewChild *visible_child;
+
+  g_assert (self->browsing_view);
+  g_assert (self->browsing_view_child);
+
+  visible_child = adw_browsing_view_get_visible_child (ADW_BROWSING_VIEW (self->browsing_view));
+
+  if (visible_child == ADW_BROWSING_VIEW_CHILD (self->browsing_view_child))
+    update_back_button (self);
+}
+
+static void
+browsing_view_notify_previous_view_cb (AdwHeaderBar *self)
+{
+  AdwBrowsingViewChild *prev_child;
+
+  g_assert (self->browsing_view);
+  g_assert (self->browsing_view_child);
+
+  prev_child = adw_browsing_view_get_previous_child (ADW_BROWSING_VIEW (self->browsing_view),
+                                                     ADW_BROWSING_VIEW_CHILD (self->browsing_view_child));
+
+  if (!prev_child || gtk_widget_get_parent (GTK_WIDGET (prev_child)) != GTK_WIDGET (self->browsing_view))
+    update_back_button (self);
+}
+
+static void
+track_back_button (AdwHeaderBar *self)
+{
+  if (!self->browsing_view)
+    return;
+
+  g_signal_connect_swapped (self->browsing_view, "pushed",
+                            G_CALLBACK (browsing_view_pushed_cb), self);
+  g_signal_connect_swapped (self->browsing_view, "notify::previous-view",
+                            G_CALLBACK (browsing_view_notify_previous_view_cb), self);
+}
+
+static void
+untrack_back_button (AdwHeaderBar *self)
+{
+  if (!self->browsing_view)
+    return;
+
+  g_signal_handlers_disconnect_by_func (self->browsing_view,
+                                        browsing_view_pushed_cb, self);
+  g_signal_handlers_disconnect_by_func (self->browsing_view,
+                                        browsing_view_notify_previous_view_cb, self);
+}
+
 static void
 adw_header_bar_root (GtkWidget *widget)
 {
   AdwHeaderBar *self = ADW_HEADER_BAR (widget);
-  GtkWidget *browsing_view_child;
+  GtkWidget *browsing_view = NULL, *browsing_view_child;
 
   GTK_WIDGET_CLASS (adw_header_bar_parent_class)->root (widget);
 
   browsing_view_child = gtk_widget_get_ancestor (widget, ADW_TYPE_BROWSING_VIEW_CHILD);
 
-  if (browsing_view_child) {
+  if (browsing_view_child)
+    browsing_view = gtk_widget_get_parent (browsing_view_child);
+
+  if (!ADW_IS_BROWSING_VIEW (browsing_view))
+    browsing_view = NULL;
+
+  // TODO: if there's a way to edit hsitory at some point, this will need to
+  // be updated too
+  // TODO: need to check can-go-back once it's implemented
+
+  if (browsing_view && browsing_view_child) {
+    self->browsing_view = browsing_view;
     self->browsing_view_child = browsing_view_child;
 
     g_signal_connect_swapped (browsing_view_child, "notify::title",
                               G_CALLBACK (update_title), widget);
+
+    if (self->show_back_button)
+      track_back_button (self);
   } else {
     GtkRoot *root = gtk_widget_get_root (widget);
 
@@ -286,6 +399,7 @@ adw_header_bar_root (GtkWidget *widget)
   }
 
   update_title (ADW_HEADER_BAR (widget));
+  update_back_button (ADW_HEADER_BAR (widget));
 }
 
 static void
@@ -293,11 +407,16 @@ adw_header_bar_unroot (GtkWidget *widget)
 {
   AdwHeaderBar *self = ADW_HEADER_BAR (widget);
 
-  if (self->browsing_view_child) {
+  if (self->browsing_view && self->browsing_view_child) {
     g_signal_handlers_disconnect_by_func (self->browsing_view_child,
                                           update_title, widget);
 
+    untrack_back_button (self);
+
+    self->browsing_view = NULL;
     self->browsing_view_child = NULL;
+
+    update_back_button (self);
   } else {
     g_signal_handlers_disconnect_by_func (gtk_widget_get_root (widget),
                                           update_title, widget);
@@ -352,6 +471,9 @@ adw_header_bar_get_property (GObject    *object,
   case PROP_SHOW_END_TITLE_BUTTONS:
     g_value_set_boolean (value, adw_header_bar_get_show_end_title_buttons (self));
     break;
+  case PROP_SHOW_BACK_BUTTON:
+    g_value_set_boolean (value, adw_header_bar_get_show_back_button (self));
+    break;
   case PROP_DECORATION_LAYOUT:
     g_value_set_string (value, adw_header_bar_get_decoration_layout (self));
     break;
@@ -382,6 +504,9 @@ adw_header_bar_set_property (GObject      *object,
   case PROP_SHOW_END_TITLE_BUTTONS:
     adw_header_bar_set_show_end_title_buttons (self, g_value_get_boolean (value));
     break;
+  case PROP_SHOW_BACK_BUTTON:
+    adw_header_bar_set_show_back_button (self, g_value_get_boolean (value));
+    break;
   case PROP_DECORATION_LAYOUT:
     adw_header_bar_set_decoration_layout (self, g_value_get_string (value));
     break;
@@ -474,6 +599,18 @@ adw_header_bar_class_init (AdwHeaderBarClass *class)
                           TRUE,
                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
 
+  /**
+   * AdwHeaderBar:show-back-button: (attributes org.gtk.Property.get=adw_header_bar_get_show_back_button 
org.gtk.Property.set=adw_header_bar_set_show_back_button)
+   *
+   * TODO
+   *
+   * Since: 1.3
+   */
+  props[PROP_SHOW_BACK_BUTTON] =
+    g_param_spec_boolean ("show-back-button", NULL, NULL,
+                          TRUE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
   /**
    * AdwHeaderBar:decoration-layout: (attributes org.gtk.Property.get=adw_header_bar_get_decoration_layout 
org.gtk.Property.set=adw_header_bar_set_decoration_layout)
    *
@@ -524,6 +661,7 @@ adw_header_bar_init (AdwHeaderBar *self)
   self->decoration_layout = NULL;
   self->show_start_title_buttons = TRUE;
   self->show_end_title_buttons = TRUE;
+  self->show_back_button = TRUE;
 
   self->handle = gtk_window_handle_new ();
   gtk_widget_set_parent (self->handle, GTK_WIDGET (self));
@@ -852,6 +990,57 @@ adw_header_bar_set_show_end_title_buttons (AdwHeaderBar *self,
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SHOW_END_TITLE_BUTTONS]);
 }
 
+/**
+ * adw_header_bar_get_show_back_button: (attributes org.gtk.Method.get_property=show-back-button)
+ * @self: a header bar
+ *
+ * TODO
+ *
+ * Returns: TODO
+ *
+ * Since: 1.3
+ */
+gboolean
+adw_header_bar_get_show_back_button (AdwHeaderBar *self)
+{
+  g_return_val_if_fail (ADW_IS_HEADER_BAR (self), FALSE);
+
+  return self->show_back_button;
+}
+
+/**
+ * adw_header_bar_set_show_back_button: (attributes org.gtk.Method.set_property=show-back-button)
+ * @self: a header bar
+ * @show_back_button: TODO
+ *
+ * TODO
+ *
+ * Since: 1.3
+ */
+void
+adw_header_bar_set_show_back_button (AdwHeaderBar *self,
+                                     gboolean      show_back_button)
+{
+  g_return_if_fail (ADW_IS_HEADER_BAR (self));
+
+  show_back_button = !!show_back_button;
+
+  if (self->show_back_button == show_back_button)
+    return;
+
+  self->show_back_button = show_back_button;
+
+  if (self->show_back_button)
+    track_back_button (self);
+  else
+    untrack_back_button (self);
+
+  if (self->start_box)
+    update_back_button (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SHOW_BACK_BUTTON]);
+}
+
 /**
  * adw_header_bar_get_decoration_layout: (attributes org.gtk.Method.get_property=decoration-layout)
  * @self: a header bar
diff --git a/src/adw-header-bar.h b/src/adw-header-bar.h
index cd18885e..acfa29c2 100644
--- a/src/adw-header-bar.h
+++ b/src/adw-header-bar.h
@@ -72,6 +72,12 @@ ADW_AVAILABLE_IN_ALL
 void     adw_header_bar_set_show_end_title_buttons (AdwHeaderBar *self,
                                                     gboolean      setting);
 
+ADW_AVAILABLE_IN_ALL
+gboolean adw_header_bar_get_show_back_button (AdwHeaderBar *self);
+ADW_AVAILABLE_IN_ALL
+void     adw_header_bar_set_show_back_button (AdwHeaderBar *self,
+                                              gboolean      show_back_button);
+
 ADW_AVAILABLE_IN_ALL
 const char *adw_header_bar_get_decoration_layout (AdwHeaderBar *self);
 ADW_AVAILABLE_IN_ALL


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