[gnome-builder/wip/gtk4-port: 67/94] gui: rebase frame on PanelFrame




commit b2039e872a259acb9bcc682fbb9c6694ad6711dd
Author: Christian Hergert <chergert redhat com>
Date:   Fri Sep 24 15:54:08 2021 -0700

    gui: rebase frame on PanelFrame
    
    This also moves IdePage to be based on PanelWidget, which is necessary to
    place them within PanelFrame.
    
    PanelFrame has been made to be subclassable upstream so that we can put
    our addins inside our subclass.

 src/libide/gui/ide-frame-actions.c      |  429 ----------
 src/libide/gui/ide-frame-addin.h        |   13 +-
 src/libide/gui/ide-frame-empty-state.c  |   46 --
 src/libide/gui/ide-frame-empty-state.h  |   37 -
 src/libide/gui/ide-frame-empty-state.ui |  106 ---
 src/libide/gui/ide-frame-header.c       |  827 --------------------
 src/libide/gui/ide-frame-header.h       |   44 --
 src/libide/gui/ide-frame-header.ui      |  112 ---
 src/libide/gui/ide-frame-shortcuts.c    |  113 ---
 src/libide/gui/ide-frame-wrapper.c      |  132 ----
 src/libide/gui/ide-frame-wrapper.h      |   31 -
 src/libide/gui/ide-frame.c              | 1292 ++-----------------------------
 src/libide/gui/ide-frame.h              |   60 +-
 src/libide/gui/ide-frame.ui             |   55 --
 src/libide/gui/ide-gui-private.h        |   42 +-
 src/libide/gui/ide-page.c               |   20 +-
 src/libide/gui/ide-page.h               |    7 +-
 src/libide/gui/meson.build              |    9 -
 18 files changed, 66 insertions(+), 3309 deletions(-)
---
diff --git a/src/libide/gui/ide-frame-addin.h b/src/libide/gui/ide-frame-addin.h
index 04648b435..1389af605 100644
--- a/src/libide/gui/ide-frame-addin.h
+++ b/src/libide/gui/ide-frame-addin.h
@@ -24,9 +24,6 @@
 # error "Only <libide-gui.h> can be included directly."
 #endif
 
-#include <gtk/gtk.h>
-#include <libide-core.h>
-
 #include "ide-frame.h"
 #include "ide-page.h"
 
@@ -34,7 +31,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_FRAME_ADDIN (ide_frame_addin_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_INTERFACE (IdeFrameAddin, ide_frame_addin, IDE, FRAME_ADDIN, GObject)
 
 struct _IdeFrameAddinInterface
@@ -49,16 +46,16 @@ struct _IdeFrameAddinInterface
                     IdePage       *page);
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_frame_addin_load                (IdeFrameAddin *self,
                                                     IdeFrame      *frame);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_frame_addin_unload              (IdeFrameAddin *self,
                                                     IdeFrame      *frame);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void           ide_frame_addin_set_page            (IdeFrameAddin *self,
                                                     IdePage       *page);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeFrameAddin *ide_frame_addin_find_by_module_name (IdeFrame      *frame,
                                                     const gchar   *module_name);
 
diff --git a/src/libide/gui/ide-frame.c b/src/libide/gui/ide-frame.c
index 793269914..e5cfb97c7 100644
--- a/src/libide/gui/ide-frame.c
+++ b/src/libide/gui/ide-frame.c
@@ -1,6 +1,6 @@
 /* ide-frame.c
  *
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2017-2021 Christian Hergert <chergert redhat com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,20 +22,14 @@
 
 #include "config.h"
 
-#include <dazzle.h>
 #include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
 #include <libide-core.h>
 #include <libide-threading.h>
-#include <libpeas/peas.h>
 
 #include "ide-frame.h"
 #include "ide-frame-addin.h"
-#include "ide-frame-header.h"
-#include "ide-frame-wrapper.h"
-#include "ide-gui-private.h"
-
-#define TRANSITION_DURATION 300
-#define DISTANCE_THRESHOLD(alloc) (MIN(250, (gint)((alloc)->width * .333)))
 
 /**
  * SECTION:ide-frame
@@ -48,140 +42,15 @@
  *
  * If there are no #IdePage visibile, then an empty state widget is
  * displayed with some common information for the user.
- *
- * To simplify integration with other systems, #IdeFrame implements
- * the #GListModel interface for each of the #IdePage.
- *
- * Since: 3.32
  */
 
-typedef struct
-{
-  DzlBindingGroup      *bindings;
-  DzlSignalGroup       *signals;
-  GPtrArray            *pages;
-  GPtrArray            *in_transition;
-  PeasExtensionSet     *addins;
-
-  /*
-   * Our gestures are used to do interactive moves when the user
-   * does a three finger swipe. We create the dummy gesture to
-   * ensure things work, because it for some reason does not without
-   * the dummy gesture set.
-   *
-   * https://bugzilla.gnome.org/show_bug.cgi?id=788914
-   */
-  GtkGesture           *dummy;
-  GtkGesture           *pan;
-  DzlBoxTheatric       *pan_theatric;
-  IdePage              *pan_page;
-
-  /* Template references */
-  GtkBox               *empty_placeholder;
-  DzlEmptyState        *failed_state;
-  IdeFrameHeader       *header;
-  GtkStack             *stack;
-  GtkStack             *top_stack;
-  GtkEventBox          *event_box;
-} IdeFramePrivate;
-
-typedef struct
+struct _IdeFrame
 {
-  IdeFrame *source;
-  IdeFrame *dest;
-  IdePage  *page;
-  DzlBoxTheatric *theatric;
-} AnimationState;
-
-enum {
-  PROP_0,
-  PROP_HAS_VIEW,
-  PROP_VISIBLE_CHILD,
-  N_PROPS
+  PanelFrame        parent_instance;
+  PeasExtensionSet *addins;
 };
 
-enum {
-  CHANGE_CURRENT_PAGE,
-  N_SIGNALS
-};
-
-static void buildable_iface_init     (GtkBuildableIface   *iface);
-static void list_model_iface_init    (GListModelInterface *iface);
-static void animation_state_complete (gpointer             data);
-
-G_DEFINE_TYPE_WITH_CODE (IdeFrame, ide_frame, GTK_TYPE_BOX,
-                         G_ADD_PRIVATE (IdeFrame)
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init)
-                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
-
-static GParamSpec *properties [N_PROPS];
-static guint signals [N_SIGNALS];
-
-static inline gboolean
-is_uninitialized (GtkAllocation *alloc)
-{
-  return (alloc->x == -1 && alloc->y == -1 &&
-          alloc->width == 1 && alloc->height == 1);
-}
-
-static void
-ide_frame_set_cursor (IdeFrame    *self,
-                      const gchar *name)
-{
-  GdkWindow *window;
-  GdkDisplay *display;
-  GdkCursor *cursor;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (name != NULL);
-
-  window = gtk_widget_get_window (GTK_WIDGET (self));
-  display = gtk_widget_get_display (GTK_WIDGET (self));
-  cursor = gdk_cursor_new_from_name (display, name);
-
-  gdk_window_set_cursor (window, cursor);
-
-  g_clear_object (&cursor);
-}
-
-static void
-ide_frame_page_failed (IdeFrame   *self,
-                       GParamSpec *pspec,
-                       IdePage    *page)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (IDE_IS_PAGE (page));
-
-  if (ide_page_get_failed (page))
-    gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
-  else
-    gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
-}
-
-static void
-ide_frame_bindings_notify_source (IdeFrame        *self,
-                                  GParamSpec      *pspec,
-                                  DzlBindingGroup *bindings)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  GObject *source;
-
-  g_assert (DZL_IS_BINDING_GROUP (bindings));
-  g_assert (pspec != NULL);
-  g_assert (IDE_IS_FRAME (self));
-
-  source = dzl_binding_group_get_source (bindings);
-
-  if (source == NULL)
-    {
-      _ide_frame_header_set_title (priv->header, _("No Open Pages"));
-      _ide_frame_header_set_modified (priv->header, FALSE);
-      _ide_frame_header_set_background_rgba (priv->header, NULL);
-      _ide_frame_header_set_foreground_rgba (priv->header, NULL);
-    }
-}
+G_DEFINE_TYPE (IdeFrame, ide_frame, PANEL_TYPE_FRAME)
 
 static void
 ide_frame_notify_addin_of_page (PeasExtensionSet *set,
@@ -202,215 +71,28 @@ ide_frame_notify_addin_of_page (PeasExtensionSet *set,
 
 static void
 ide_frame_notify_visible_child (IdeFrame   *self,
-                                GParamSpec *pspec,
-                                GtkStack   *stack)
+                                GParamSpec *pspec)
 {
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  GtkWidget *visible_child;
+  PanelWidget *visible_child;
 
   g_assert (IDE_IS_FRAME (self));
-  g_assert (GTK_IS_STACK (stack));
-
-  if (gtk_widget_in_destruction (GTK_WIDGET (self)))
-    return;
 
-  if ((visible_child = gtk_stack_get_visible_child (priv->stack)))
-    {
-      if (gtk_widget_in_destruction (visible_child))
-        visible_child = NULL;
-    }
-
-  /*
+#if 0
+  /* FIXME: We can probably this differently in GTK 4
+   *
    * Mux/Proxy actions to our level so that they also be activated
    * from the header bar without any weirdness by the View.
    */
   dzl_gtk_widget_mux_action_groups (GTK_WIDGET (self), visible_child,
                                     "IDE_FRAME_MUXED_ACTION");
+#endif
 
-  /* Update our bindings targets */
-  dzl_binding_group_set_source (priv->bindings, visible_child);
-  dzl_signal_group_set_target (priv->signals, visible_child);
-
-  /* Show either the empty state, failed state, or actual page */
-  if (visible_child != NULL &&
-      ide_page_get_failed (IDE_PAGE (visible_child)))
-    gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
-  else if (visible_child != NULL)
-    gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
-  else
-    gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->empty_placeholder));
+  visible_child = panel_frame_get_visible_child (PANEL_FRAME (self));
 
-  /* Allow the header to update settings */
-  _ide_frame_header_update (priv->header, IDE_PAGE (visible_child));
-
-  /* Ensure action state is up to date */
-  _ide_frame_update_actions (self);
-
-  if (priv->addins != NULL)
-    peas_extension_set_foreach (priv->addins,
+  if (self->addins != NULL)
+    peas_extension_set_foreach (self->addins,
                                 ide_frame_notify_addin_of_page,
                                 visible_child);
-
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_CHILD]);
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_VIEW]);
-}
-
-static void
-collect_widgets (GtkWidget *widget,
-                 gpointer   user_data)
-{
-  g_ptr_array_add (user_data, widget);
-}
-
-static void
-ide_frame_change_current_page (IdeFrame *self,
-                               gint      direction)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  g_autoptr(GPtrArray) ar = NULL;
-  GtkWidget *visible_child;
-  gint position = 0;
-
-  g_assert (IDE_IS_FRAME (self));
-
-  if (direction < -1)
-    direction = -1;
-  else if (direction > 1)
-    direction = 1;
-
-  visible_child = gtk_stack_get_visible_child (priv->stack);
-
-  if (visible_child == NULL)
-    return;
-
-  gtk_container_child_get (GTK_CONTAINER (priv->stack), visible_child,
-                           "position", &position,
-                           NULL);
-
-  ar = g_ptr_array_new ();
-  gtk_container_foreach (GTK_CONTAINER (priv->stack), collect_widgets, ar);
-  if (ar->len == 0)
-    g_return_if_reached ();
-
-  position = (position + (int)ar->len - direction) % (int)ar->len;
-
-  visible_child = g_ptr_array_index (ar, position);
-  gtk_stack_set_visible_child (priv->stack, visible_child);
-}
-
-static void
-ide_frame_add (GtkContainer *container,
-               GtkWidget    *widget)
-{
-  IdeFrame *self = (IdeFrame *)container;
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-
-  if (IDE_IS_PAGE (widget))
-    gtk_container_add (GTK_CONTAINER (priv->stack), widget);
-  else
-    GTK_CONTAINER_CLASS (ide_frame_parent_class)->add (container, widget);
-
-  gtk_widget_queue_resize (GTK_WIDGET (self));
-}
-
-static void
-ide_frame_page_added (IdeFrame *self,
-                      IdePage  *page)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  guint position;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (IDE_IS_PAGE (page));
-
-  /*
-   * Make sure that the header has dismissed all of the popovers immediately.
-   * We don't want them lingering while we do other UI work which might want to
-   * grab focus, etc.
-   */
-  _ide_frame_header_popdown (priv->header);
-
-  /* Notify GListModel consumers of the new page and it's position within
-   * our stack of page widgets.
-   */
-  position = priv->pages->len;
-  g_ptr_array_add (priv->pages, page);
-  g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
-
-  /*
-   * Now ensure that the page is displayed and focus the widget so the
-   * user can immediately start typing.
-   */
-  ide_frame_set_visible_child (self, page);
-  gtk_widget_grab_focus (GTK_WIDGET (page));
-}
-
-static void
-ide_frame_page_removed (IdeFrame *self,
-                        IdePage  *page)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (IDE_IS_PAGE (page));
-
-  if (priv->pages != NULL)
-    {
-      guint position = 0;
-
-      /* If this is the last page, hide the popdown now.  We use our hide
-       * variant instead of popdown so that we don't have jittery animations.
-       */
-      if (priv->pages->len == 1)
-        _ide_frame_header_hide (priv->header);
-
-      /*
-       * Only remove the page if it is not in transition. We hold onto the
-       * page during the transition so that we keep the list stable.
-       */
-      if (!g_ptr_array_find_with_equal_func (priv->in_transition, page, NULL, &position))
-        {
-          for (guint i = 0; i < priv->pages->len; i++)
-            {
-              if ((gpointer)page == g_ptr_array_index (priv->pages, i))
-                {
-                  g_ptr_array_remove_index (priv->pages, i);
-                  g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
-                }
-            }
-        }
-    }
-}
-
-static void
-ide_frame_real_agree_to_close_async (IdeFrame            *self,
-                                     GCancellable        *cancellable,
-                                     GAsyncReadyCallback  callback,
-                                     gpointer             user_data)
-{
-  g_autoptr(IdeTask) task = NULL;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_frame_real_agree_to_close_async);
-  ide_task_set_priority (task, G_PRIORITY_LOW);
-  ide_task_return_boolean (task, TRUE);
-}
-
-static gboolean
-ide_frame_real_agree_to_close_finish (IdeFrame      *self,
-                                      GAsyncResult  *result,
-                                      GError       **error)
-{
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (IDE_IS_TASK (result));
-
-  return ide_task_propagate_boolean (IDE_TASK (result), error);
 }
 
 static void
@@ -430,7 +112,7 @@ ide_frame_addin_added (PeasExtensionSet *set,
 
   ide_frame_addin_load (addin, self);
 
-  visible_child = ide_frame_get_visible_child (self);
+  visible_child = IDE_PAGE (panel_frame_get_visible_child (PANEL_FRAME (self)));
 
   if (visible_child != NULL)
     ide_frame_addin_set_page (addin, visible_child);
@@ -454,515 +136,62 @@ ide_frame_addin_removed (PeasExtensionSet *set,
   ide_frame_addin_unload (addin, self);
 }
 
-static gboolean
-ide_frame_pan_begin (IdeFrame         *self,
-                     GdkEventSequence *sequence,
-                     GtkGesturePan    *gesture)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  GtkAllocation alloc;
-  cairo_surface_t *surface = NULL;
-  GdkDevice *device;
-  IdePage *page;
-  GdkWindow *window;
-  GtkWidget *grid;
-  cairo_t *cr;
-  gdouble x, y;
-  gboolean enable_animations;
-  GdkModifierType state = 0;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (GTK_IS_GESTURE_PAN (gesture));
-  g_assert (priv->pan_theatric == NULL);
-
-  if (!(page = ide_frame_get_visible_child (self)) ||
-      !(device = gtk_gesture_get_device (GTK_GESTURE (gesture))) ||
-      !(window = gtk_widget_get_window (GTK_WIDGET (page))))
-    goto failure;
-
-  gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
-  gdk_device_get_state (device, window, NULL, &state);
-
-  g_object_get (gtk_settings_get_default (),
-                "gtk-enable-animations", &enable_animations,
-                NULL);
-
-  if (sequence != NULL ||
-      !enable_animations ||
-      (state & GDK_SHIFT_MASK) == 0 ||
-      is_uninitialized (&alloc) ||
-      NULL == (surface = gdk_window_create_similar_surface (window,
-                                                            CAIRO_CONTENT_COLOR,
-                                                            alloc.width,
-                                                            alloc.height)))
-    goto failure;
-
-  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
-
-  cr = cairo_create (surface);
-  gtk_widget_draw (GTK_WIDGET (page), cr);
-  cairo_destroy (cr);
-
-  grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
-  gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
-                                    &alloc.x, &alloc.y);
-
-  priv->pan_page = g_object_ref (page);
-  priv->pan_theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
-                                     "surface", surface,
-                                     "target", grid,
-                                     "x", alloc.x + (gint)x,
-                                     "y", alloc.y,
-                                     "width", alloc.width,
-                                     "height", alloc.height,
-                                     NULL);
-
-  g_clear_pointer (&surface, cairo_surface_destroy);
-
-  /* Hide the page while we begin the possible transition to another
-   * layout stack.
-   */
-  gtk_widget_hide (GTK_WIDGET (priv->pan_page));
-
-  /*
-   * Hide the mouse cursor until ide_frame_pan_end() is called.
-   * It can be distracting otherwise (and we want to warp it to the new
-   * grid column too).
-   */
-  ide_frame_set_cursor (self, "none");
-
-  IDE_RETURN (TRUE);
-
-failure:
-  if (sequence != NULL)
-    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-
-  IDE_RETURN (FALSE);
-}
-
-static void
-ide_frame_pan_update (IdeFrame         *self,
-                      GdkEventSequence *sequence,
-                      GtkGestureSwipe  *gesture)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  GtkAllocation alloc;
-  GtkWidget *grid;
-  gdouble x, y;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (GTK_IS_GESTURE_PAN (gesture));
-  g_assert (!priv->pan_theatric || DZL_IS_BOX_THEATRIC (priv->pan_theatric));
-
-  if (priv->pan_theatric == NULL)
-    {
-      if (sequence != NULL)
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-      IDE_EXIT;
-    }
-
-  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
-  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
-  grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
-  gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
-                                    &alloc.x, &alloc.y);
-
-  g_object_set (priv->pan_theatric,
-                "x", alloc.x + (gint)x,
-                NULL);
-
-  IDE_EXIT;
-}
-
-static void
-ide_frame_pan_end (IdeFrame         *self,
-                   GdkEventSequence *sequence,
-                   GtkGesturePan    *gesture)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  IdeFramePrivate *dest_priv;
-  IdeFrame *dest;
-  GtkAllocation alloc;
-  GtkWidget *grid;
-  GtkWidget *column;
-  gdouble x, y;
-  gint direction;
-  gint index = 0;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (GTK_IS_GESTURE_PAN (gesture));
-
-  if (priv->pan_theatric == NULL || priv->pan_page == NULL)
-    IDE_GOTO (cleanup);
-
-  gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
-  gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
-
-  if (x > DISTANCE_THRESHOLD (&alloc))
-    direction = 1;
-  else if (x < -DISTANCE_THRESHOLD (&alloc))
-    direction = -1;
-  else
-    direction = 0;
-
-  grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
-  g_assert (grid != NULL);
-  g_assert (IDE_IS_GRID (grid));
-
-  column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN);
-  g_assert (column != NULL);
-  g_assert (IDE_IS_GRID_COLUMN (column));
-
-  gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
-                           "index", &index,
-                           NULL);
-
-  dest = _ide_grid_get_nth_stack (IDE_GRID (grid), index + direction);
-  dest_priv = ide_frame_get_instance_private (dest);
-  g_assert (dest != NULL);
-  g_assert (IDE_IS_FRAME (dest));
-
-  gtk_widget_get_allocation (GTK_WIDGET (dest), &alloc);
-
-  if (!is_uninitialized (&alloc))
-    {
-      AnimationState *state;
-
-      state = g_slice_new0 (AnimationState);
-      state->source = g_object_ref (self);
-      state->dest = g_object_ref (dest);
-      state->page = g_object_ref (priv->pan_page);
-      state->theatric = g_object_ref (priv->pan_theatric);
-
-      gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->top_stack), grid, 0, 0,
-                                        &alloc.x, &alloc.y);
-
-      /*
-       * Use EASE_OUT_CUBIC, because user initiated the beginning of the
-       * acceleration curve just by swiping. No need to duplicate.
-       */
-      dzl_object_animate_full (state->theatric,
-                               DZL_ANIMATION_EASE_OUT_CUBIC,
-                               TRANSITION_DURATION,
-                               gtk_widget_get_frame_clock (GTK_WIDGET (self)),
-                               animation_state_complete,
-                               state,
-                               "x", alloc.x,
-                               "width", alloc.width,
-                               NULL);
-
-      if (dest != self)
-        {
-          g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_page));
-          gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_page));
-        }
-
-      IDE_TRACE_MSG ("Animating transition to %s column",
-                     dest != self ? "another" : "same");
-    }
-  else
-    {
-      g_autoptr(IdePage) page = g_object_ref (priv->pan_page);
-
-      IDE_TRACE_MSG ("Moving page to a previously non-existant column");
-
-      gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
-      gtk_widget_show (GTK_WIDGET (page));
-      gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
-    }
-
-cleanup:
-  g_clear_object (&priv->pan_theatric);
-  g_clear_object (&priv->pan_page);
-
-  gtk_widget_queue_draw (gtk_widget_get_toplevel (GTK_WIDGET (self)));
-
-  ide_frame_set_cursor (self, "arrow");
-
-  IDE_EXIT;
-}
-
 static void
 ide_frame_constructed (GObject *object)
 {
   IdeFrame *self = (IdeFrame *)object;
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
 
   g_assert (IDE_IS_FRAME (self));
 
   G_OBJECT_CLASS (ide_frame_parent_class)->constructed (object);
 
-  priv->addins = peas_extension_set_new (peas_engine_get_default (),
+  self->addins = peas_extension_set_new (peas_engine_get_default (),
                                          IDE_TYPE_FRAME_ADDIN,
                                          NULL);
 
-  g_signal_connect (priv->addins,
+  g_signal_connect (self->addins,
                     "extension-added",
                     G_CALLBACK (ide_frame_addin_added),
                     self);
 
-  g_signal_connect (priv->addins,
+  g_signal_connect (self->addins,
                     "extension-removed",
                     G_CALLBACK (ide_frame_addin_removed),
                     self);
 
-  peas_extension_set_foreach (priv->addins,
+  peas_extension_set_foreach (self->addins,
                               ide_frame_addin_added,
                               self);
-
-  gtk_widget_add_events (GTK_WIDGET (priv->event_box), GDK_TOUCH_MASK);
-  priv->pan = g_object_new (GTK_TYPE_GESTURE_PAN,
-                            "widget", priv->event_box,
-                            "orientation", GTK_ORIENTATION_HORIZONTAL,
-                            "n-points", 3,
-                            NULL);
-  g_signal_connect_swapped (priv->pan,
-                            "begin",
-                            G_CALLBACK (ide_frame_pan_begin),
-                            self);
-  g_signal_connect_swapped (priv->pan,
-                            "update",
-                            G_CALLBACK (ide_frame_pan_update),
-                            self);
-  g_signal_connect_swapped (priv->pan,
-                            "end",
-                            G_CALLBACK (ide_frame_pan_end),
-                            self);
-  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan),
-                                              GTK_PHASE_BUBBLE);
-
-  /*
-   * FIXME: Our priv->pan gesture does not activate unless we add another
-   *        dummy gesture. I currently have no idea why that is.
-   *
-   *        https://bugzilla.gnome.org/show_bug.cgi?id=788914
-   */
-  priv->dummy = gtk_gesture_rotate_new (GTK_WIDGET (priv->event_box));
-  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->dummy),
-                                              GTK_PHASE_BUBBLE);
-}
-
-static void
-ide_frame_grab_focus (GtkWidget *widget)
-{
-  IdeFrame *self = (IdeFrame *)widget;
-  IdePage *child;
-
-  g_assert (IDE_IS_FRAME (self));
-
-  child = ide_frame_get_visible_child (self);
-
-  if (child != NULL)
-    gtk_widget_grab_focus (GTK_WIDGET (child));
-  else
-    GTK_WIDGET_CLASS (ide_frame_parent_class)->grab_focus (widget);
 }
 
 static void
-ide_frame_destroy (GtkWidget *widget)
+ide_frame_dispose (GObject *object)
 {
-  IdeFrame *self = (IdeFrame *)widget;
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+  IdeFrame *self = (IdeFrame *)object;
 
   g_assert (IDE_IS_FRAME (self));
 
-  g_clear_object (&priv->addins);
-
-  g_clear_pointer (&priv->in_transition, g_ptr_array_unref);
+  g_clear_object (&self->addins);
 
-  if (priv->pages != NULL)
-    {
-      g_list_model_items_changed (G_LIST_MODEL (self), 0, priv->pages->len, 0);
-      g_clear_pointer (&priv->pages, g_ptr_array_unref);
-    }
-
-  if (priv->bindings != NULL)
-    {
-      dzl_binding_group_set_source (priv->bindings, NULL);
-      g_clear_object (&priv->bindings);
-    }
-
-  if (priv->signals != NULL)
-    {
-      dzl_signal_group_set_target (priv->signals, NULL);
-      g_clear_object (&priv->signals);
-    }
-
-  g_clear_object (&priv->pan);
-
-  GTK_WIDGET_CLASS (ide_frame_parent_class)->destroy (widget);
-}
-
-static void
-ide_frame_get_property (GObject    *object,
-                        guint       prop_id,
-                        GValue     *value,
-                        GParamSpec *pspec)
-{
-  IdeFrame *self = IDE_FRAME (object);
-
-  switch (prop_id)
-    {
-    case PROP_HAS_VIEW:
-      g_value_set_boolean (value, ide_frame_get_has_page (self));
-      break;
-
-    case PROP_VISIBLE_CHILD:
-      g_value_set_object (value, ide_frame_get_visible_child (self));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-ide_frame_set_property (GObject      *object,
-                        guint         prop_id,
-                        const GValue *value,
-                        GParamSpec   *pspec)
-{
-  IdeFrame *self = IDE_FRAME (object);
-
-  switch (prop_id)
-    {
-    case PROP_VISIBLE_CHILD:
-      ide_frame_set_visible_child (self, g_value_get_object (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
+  G_OBJECT_CLASS (ide_frame_parent_class)->dispose (object);
 }
 
 static void
 ide_frame_class_init (IdeFrameClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
+  object_class->dispose = ide_frame_dispose;
   object_class->constructed = ide_frame_constructed;
-  object_class->get_property = ide_frame_get_property;
-  object_class->set_property = ide_frame_set_property;
-
-  widget_class->destroy = ide_frame_destroy;
-  widget_class->grab_focus = ide_frame_grab_focus;
-
-  container_class->add = ide_frame_add;
-
-  klass->agree_to_close_async = ide_frame_real_agree_to_close_async;
-  klass->agree_to_close_finish = ide_frame_real_agree_to_close_finish;
-
-  properties [PROP_HAS_VIEW] =
-    g_param_spec_boolean ("has-page", NULL, NULL,
-                          FALSE,
-                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-  properties [PROP_VISIBLE_CHILD] =
-    g_param_spec_object ("visible-child",
-                         "Visible Child",
-                         "The current page to be displayed",
-                         IDE_TYPE_PAGE,
-                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_properties (object_class, N_PROPS, properties);
-
-  signals [CHANGE_CURRENT_PAGE] =
-    g_signal_new_class_handler ("change-current-page",
-                                G_TYPE_FROM_CLASS (klass),
-                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-                                G_CALLBACK (ide_frame_change_current_page),
-                                NULL, NULL,
-                                g_cclosure_marshal_VOID__INT,
-                                G_TYPE_NONE, 1, G_TYPE_INT);
-
-  gtk_widget_class_set_css_name (widget_class, "ideframe");
-  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-frame.ui");
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, empty_placeholder);
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, failed_state);
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, header);
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, stack);
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, top_stack);
-  gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, event_box);
-
-  g_type_ensure (IDE_TYPE_FRAME_HEADER);
-  g_type_ensure (IDE_TYPE_FRAME_WRAPPER);
-  g_type_ensure (IDE_TYPE_SHORTCUT_LABEL);
 }
 
 static void
 ide_frame_init (IdeFrame *self)
 {
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  gtk_widget_init_template (GTK_WIDGET (self));
-
-  _ide_frame_init_actions (self);
-  _ide_frame_init_shortcuts (self);
-
-  priv->pages = g_ptr_array_new ();
-  priv->in_transition = g_ptr_array_new_with_free_func (g_object_unref);
-
-  priv->signals = dzl_signal_group_new (IDE_TYPE_PAGE);
-
-  dzl_signal_group_connect_swapped (priv->signals,
-                                    "notify::failed",
-                                    G_CALLBACK (ide_frame_page_failed),
-                                    self);
-
-  priv->bindings = dzl_binding_group_new ();
-
-  g_signal_connect_object (priv->bindings,
-                           "notify::source",
-                           G_CALLBACK (ide_frame_bindings_notify_source),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  dzl_binding_group_bind (priv->bindings, "title",
-                          priv->header, "title",
-                          G_BINDING_SYNC_CREATE);
-
-  dzl_binding_group_bind (priv->bindings, "modified",
-                          priv->header, "modified",
-                          G_BINDING_SYNC_CREATE);
-
-  dzl_binding_group_bind (priv->bindings, "primary-color-bg",
-                          priv->header, "background-rgba",
-                          G_BINDING_SYNC_CREATE);
-
-  dzl_binding_group_bind (priv->bindings, "primary-color-fg",
-                          priv->header, "foreground-rgba",
-                          G_BINDING_SYNC_CREATE);
-
-  g_signal_connect_object (priv->stack,
-                           "notify::visible-child",
-                           G_CALLBACK (ide_frame_notify_visible_child),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (priv->stack,
-                           "add",
-                           G_CALLBACK (ide_frame_page_added),
-                           self,
-                           G_CONNECT_SWAPPED | G_CONNECT_AFTER);
-
-  g_signal_connect_object (priv->stack,
-                           "remove",
-                           G_CALLBACK (ide_frame_page_removed),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  _ide_frame_header_set_pages (priv->header, G_LIST_MODEL (self));
-  _ide_frame_header_update (priv->header, NULL);
+  g_signal_connect (self,
+                    "notify::visible-child",
+                    G_CALLBACK (ide_frame_notify_visible_child),
+                    NULL);
 }
 
 GtkWidget *
@@ -971,387 +200,6 @@ ide_frame_new (void)
   return g_object_new (IDE_TYPE_FRAME, NULL);
 }
 
-/**
- * ide_frame_set_visible_child:
- * @self: a #IdeFrame
- *
- * Sets the current page for the stack.
- *
- * Since: 3.32
- */
-void
-ide_frame_set_visible_child (IdeFrame *self,
-                             IdePage  *page)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (IDE_IS_PAGE (page));
-  g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (page)) == (GtkWidget *)priv->stack);
-
-  gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (page));
-}
-
-/**
- * ide_frame_get_visible_child:
- * @self: a #IdeFrame
- *
- * Gets the visible #IdePage if there is one; otherwise %NULL.
- *
- * Returns: (nullable) (transfer none): An #IdePage or %NULL
- *
- * Since: 3.32
- */
-IdePage *
-ide_frame_get_visible_child (IdeFrame *self)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
-
-  return IDE_PAGE (gtk_stack_get_visible_child (priv->stack));
-}
-
-/**
- * ide_frame_get_titlebar:
- * @self: a #IdeFrame
- *
- * Gets the #IdeFrameHeader header that is at the top of the stack.
- *
- * Returns: (transfer none) (type IdeFrameHeader): The layout stack header.
- *
- * Since: 3.32
- */
-GtkWidget *
-ide_frame_get_titlebar (IdeFrame *self)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
-
-  return GTK_WIDGET (priv->header);
-}
-
-/**
- * ide_frame_get_has_page:
- * @self: an #IdeFrame
- *
- * Gets the "has-page" property.
- *
- * This property is a convenience to allow widgets to easily bind
- * properties based on whether or not a page is visible in the stack.
- *
- * Returns: %TRUE if the stack has a page
- *
- * Since: 3.32
- */
-gboolean
-ide_frame_get_has_page (IdeFrame *self)
-{
-  IdePage *visible_child;
-
-  g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
-
-  visible_child = ide_frame_get_visible_child (self);
-
-  return visible_child != NULL;
-}
-
-static void
-ide_frame_close_page_cb (GObject      *object,
-                         GAsyncResult *result,
-                         gpointer      user_data)
-{
-  IdePage *page = (IdePage *)object;
-  g_autoptr(IdeFrame) self = user_data;
-  g_autoptr(GError) error = NULL;
-  GtkWidget *toplevel;
-  GtkWidget *focus;
-  gboolean had_focus = FALSE;
-
-  g_assert (IDE_IS_PAGE (page));
-  g_assert (G_IS_ASYNC_RESULT (result));
-  g_assert (IDE_IS_FRAME (self));
-
-  if (!ide_page_agree_to_close_finish (page, result, &error))
-    {
-      g_message ("%s does not agree to close: %s",
-                 G_OBJECT_TYPE_NAME (page),
-                 error ? error->message : "No reason");
-      return;
-    }
-
-  /* Keep track of whether or not the widget had focus (which
-   * would happen if we were activated from a keybinding.
-   */
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
-  if (GTK_IS_WINDOW (toplevel) &&
-      NULL != (focus = gtk_window_get_focus (GTK_WINDOW (toplevel))) &&
-      (focus == GTK_WIDGET (page) ||
-       gtk_widget_is_ancestor (focus, GTK_WIDGET (page))))
-    had_focus = TRUE;
-
-  /* Now we can destroy the child */
-  gtk_widget_destroy (GTK_WIDGET (page));
-
-  /* We don't want to leave the widget focus in an indeterminate
-   * state so we immediately focus the next child in the stack.
-   * But only do so if we had focus previously.
-   */
-  if (had_focus)
-    {
-      IdePage *visible_child = ide_frame_get_visible_child (self);
-
-      if (visible_child != NULL)
-        gtk_widget_grab_focus (GTK_WIDGET (visible_child));
-    }
-}
-
-void
-_ide_frame_request_close (IdeFrame *self,
-                          IdePage  *page)
-{
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (IDE_IS_PAGE (page));
-
-  ide_page_agree_to_close_async (page,
-                                        NULL,
-                                        ide_frame_close_page_cb,
-                                        g_object_ref (self));
-}
-
-static GType
-ide_frame_get_item_type (GListModel *model)
-{
-  return IDE_TYPE_PAGE;
-}
-
-static guint
-ide_frame_get_n_items (GListModel *model)
-{
-  IdeFrame *self = (IdeFrame *)model;
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_assert (IDE_IS_FRAME (self));
-
-  return priv->pages ? priv->pages->len : 0;
-}
-
-static gpointer
-ide_frame_get_item (GListModel *model,
-                    guint       position)
-{
-  IdeFrame *self = (IdeFrame *)model;
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_assert (IDE_IS_FRAME (self));
-  g_assert (position < priv->pages->len);
-
-  return g_object_ref (g_ptr_array_index (priv->pages, position));
-}
-
-static void
-list_model_iface_init (GListModelInterface *iface)
-{
-  iface->get_n_items = ide_frame_get_n_items;
-  iface->get_item = ide_frame_get_item;
-  iface->get_item_type = ide_frame_get_item_type;
-}
-
-void
-ide_frame_agree_to_close_async (IdeFrame            *self,
-                                GCancellable        *cancellable,
-                                GAsyncReadyCallback  callback,
-                                gpointer             user_data)
-{
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  IDE_FRAME_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
-}
-
-gboolean
-ide_frame_agree_to_close_finish (IdeFrame      *self,
-                                 GAsyncResult  *result,
-                                 GError       **error)
-{
-  g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
-  return IDE_FRAME_GET_CLASS (self)->agree_to_close_finish (self, result, error);
-}
-
-static void
-animation_state_complete (gpointer data)
-{
-  IdeFramePrivate *priv;
-  AnimationState *state = data;
-
-  g_assert (state != NULL);
-  g_assert (IDE_IS_FRAME (state->source));
-  g_assert (IDE_IS_FRAME (state->dest));
-  g_assert (IDE_IS_PAGE (state->page));
-
-  /* Add the widget to the new stack */
-  if (state->dest != state->source)
-    {
-      gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->page));
-
-      /* Now remove it from our temporary transition. Be careful in case we were
-       * destroyed in the mean time.
-       */
-      priv = ide_frame_get_instance_private (state->source);
-
-      if (priv->in_transition != NULL)
-        {
-          guint position = 0;
-
-          if (g_ptr_array_find_with_equal_func (priv->pages, state->page, NULL, &position))
-            {
-              g_ptr_array_remove (priv->in_transition, state->page);
-              g_ptr_array_remove_index (priv->pages, position);
-              g_list_model_items_changed (G_LIST_MODEL (state->source), position, 1, 0);
-            }
-        }
-    }
-
-  /*
-   * We might need to reshow the widget in cases where we are in a
-   * three-finger-swipe of the page. There is also a chance that we
-   * aren't the proper visible child and that needs to be restored now.
-   */
-  gtk_widget_show (GTK_WIDGET (state->page));
-  ide_frame_set_visible_child (state->dest, state->page);
-
-  g_clear_object (&state->source);
-  g_clear_object (&state->dest);
-  g_clear_object (&state->page);
-  g_clear_object (&state->theatric);
-  g_slice_free (AnimationState, state);
-}
-
-void
-_ide_frame_transfer (IdeFrame *self,
-                     IdeFrame *dest,
-                     IdePage  *page)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-  IdeFramePrivate *dest_priv = ide_frame_get_instance_private (dest);
-  const GdkRGBA *fg;
-  const GdkRGBA *bg;
-
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (IDE_IS_FRAME (dest));
-  g_return_if_fail (IDE_IS_PAGE (page));
-  g_return_if_fail (GTK_WIDGET (priv->stack) == gtk_widget_get_parent (GTK_WIDGET (page)));
-
-  /*
-   * Inform the destination stack about our new primary colors so that it can
-   * begin a transition to the new colors. We also want to do this upfront so
-   * that we can reduce the amount of style invalidation caused during the
-   * transitions.
-   */
-
-  fg = ide_page_get_primary_color_fg (page);
-  bg = ide_page_get_primary_color_bg (page);
-  _ide_frame_header_set_foreground_rgba (dest_priv->header, fg);
-  _ide_frame_header_set_background_rgba (dest_priv->header, bg);
-
-  /*
-   * If both the old and the new stacks are mapped, we can animate
-   * between them using a snapshot of the page. Well, we also need
-   * to be sure they have a valid allocation, but that check is done
-   * slightly after this because it makes things easier.
-   */
-  if (gtk_widget_get_mapped (GTK_WIDGET (self)) &&
-      gtk_widget_get_mapped (GTK_WIDGET (dest)) &&
-      gtk_widget_get_mapped (GTK_WIDGET (page)))
-    {
-      GtkAllocation alloc, dest_alloc;
-      cairo_surface_t *surface = NULL;
-      GdkWindow *window;
-      GtkWidget *grid;
-      gboolean enable_animations;
-
-      grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
-
-      gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
-      gtk_widget_get_allocation (GTK_WIDGET (dest), &dest_alloc);
-
-      g_object_get (gtk_settings_get_default (),
-                    "gtk-enable-animations", &enable_animations,
-                    NULL);
-
-      if (enable_animations &&
-          grid != NULL &&
-          !is_uninitialized (&alloc) &&
-          !is_uninitialized (&dest_alloc) &&
-          dest_alloc.width > 0 && dest_alloc.height > 0 &&
-          NULL != (window = gtk_widget_get_window (GTK_WIDGET (page))) &&
-          NULL != (surface = gdk_window_create_similar_surface (window,
-                                                                CAIRO_CONTENT_COLOR,
-                                                                alloc.width,
-                                                                alloc.height)))
-        {
-          DzlBoxTheatric *theatric = NULL;
-          AnimationState *state;
-          cairo_t *cr;
-
-          cr = cairo_create (surface);
-          gtk_widget_draw (GTK_WIDGET (page), cr);
-          cairo_destroy (cr);
-
-          gtk_widget_translate_coordinates (GTK_WIDGET (priv->stack), grid, 0, 0,
-                                            &alloc.x, &alloc.y);
-          gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->stack), grid, 0, 0,
-                                            &dest_alloc.x, &dest_alloc.y);
-
-          theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
-                                   "surface", surface,
-                                   "height", alloc.height,
-                                   "target", grid,
-                                   "width", alloc.width,
-                                   "x", alloc.x,
-                                   "y", alloc.y,
-                                   NULL);
-
-          state = g_slice_new0 (AnimationState);
-          state->source = g_object_ref (self);
-          state->dest = g_object_ref (dest);
-          state->page = g_object_ref (page);
-          state->theatric = theatric;
-
-          dzl_object_animate_full (theatric,
-                                   DZL_ANIMATION_EASE_IN_OUT_CUBIC,
-                                   TRANSITION_DURATION,
-                                   gtk_widget_get_frame_clock (GTK_WIDGET (self)),
-                                   animation_state_complete,
-                                   state,
-                                   "x", dest_alloc.x,
-                                   "width", dest_alloc.width,
-                                   "y", dest_alloc.y,
-                                   "height", dest_alloc.height,
-                                   NULL);
-
-          /*
-           * Mark the page as in-transition so that when we remove it
-           * we can ignore the items-changed until the animation completes.
-           */
-          g_ptr_array_add (priv->in_transition, g_object_ref (page));
-          gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
-
-          cairo_surface_destroy (surface);
-
-          return;
-        }
-    }
-
-  g_object_ref (page);
-  gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
-  gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
-  g_object_unref (page);
-}
-
 /**
  * ide_frame_foreach_page:
  * @self: a #IdeFrame
@@ -1359,20 +207,26 @@ _ide_frame_transfer (IdeFrame *self,
  * @user_data: user data for @callback
  *
  * This function will call @callback for every page found in @self.
- *
- * Since: 3.32
  */
 void
-ide_frame_foreach_page (IdeFrame    *self,
-                        GtkCallback  callback,
-                        gpointer     user_data)
+ide_frame_foreach_page (IdeFrame        *self,
+                        IdePageCallback  callback,
+                        gpointer         user_data)
 {
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+  guint n_pages;
 
   g_return_if_fail (IDE_IS_FRAME (self));
   g_return_if_fail (callback != NULL);
 
-  gtk_container_foreach (GTK_CONTAINER (priv->stack), callback, user_data);
+  n_pages = panel_frame_get_n_pages (PANEL_FRAME (self));
+
+  /* Iterate backwards to allow removing of page */
+  for (guint i = n_pages; i > 0; i--)
+    {
+      PanelWidget *widget = panel_frame_get_page (PANEL_FRAME (self), i - 1);
+
+      callback (IDE_PAGE (widget), user_data);
+    }
 }
 
 /**
@@ -1388,80 +242,24 @@ ide_frame_foreach_page (IdeFrame    *self,
  * #IdeFrameAddinInterface, then %NULL is returned.
  *
  * Returns: (transfer none) (nullable): An #IdeFrameAddin or %NULL
- *
- * Since: 3.32
  */
 IdeFrameAddin *
 ide_frame_addin_find_by_module_name (IdeFrame    *frame,
                                      const gchar *module_name)
 {
-  IdeFramePrivate *priv = ide_frame_get_instance_private (frame);
   PeasExtension *ret = NULL;
   PeasPluginInfo *plugin_info;
 
   g_return_val_if_fail (IDE_IS_FRAME (frame), NULL);
-  g_return_val_if_fail (priv->addins != NULL, NULL);
+  g_return_val_if_fail (frame->addins != NULL, NULL);
   g_return_val_if_fail (module_name != NULL, NULL);
 
   plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), module_name);
 
   if (plugin_info != NULL)
-    ret = peas_extension_set_get_extension (priv->addins, plugin_info);
+    ret = peas_extension_set_get_extension (frame->addins, plugin_info);
   else
     g_warning ("No addin could be found matching module \"%s\"", module_name);
 
   return ret ? IDE_FRAME_ADDIN (ret) : NULL;
 }
-
-void
-ide_frame_add_with_depth (IdeFrame  *self,
-                          GtkWidget *widget,
-                          guint      position)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-
-  gtk_container_add_with_properties (GTK_CONTAINER (priv->stack), widget,
-                                     "position", position,
-                                     NULL);
-}
-
-static void
-ide_frame_add_child (GtkBuildable *buildable,
-                     GtkBuilder   *builder,
-                     GObject      *object,
-                     const gchar  *type)
-{
-  IdeFrame *self = (IdeFrame *)buildable;
-  GtkBuildableIface *parent = g_type_interface_peek_parent (GTK_BUILDABLE_GET_IFACE (buildable));
-
-  if (g_strcmp0 (type, "placeholder") == 0 && GTK_IS_WIDGET (object))
-    ide_frame_set_placeholder (self, GTK_WIDGET (object));
-  else
-    parent->add_child (buildable, builder, object, type);
-}
-
-static void
-buildable_iface_init (GtkBuildableIface *iface)
-{
-  iface->add_child = ide_frame_add_child;
-}
-
-void
-ide_frame_set_placeholder (IdeFrame  *self,
-                           GtkWidget *placeholder)
-{
-  IdeFramePrivate *priv = ide_frame_get_instance_private (self);
-
-  g_return_if_fail (IDE_IS_FRAME (self));
-  g_return_if_fail (!placeholder || GTK_IS_WIDGET (placeholder));
-
-  gtk_container_foreach (GTK_CONTAINER (priv->empty_placeholder),
-                         (GtkCallback) gtk_widget_destroy,
-                         NULL);
-
-  if (placeholder != NULL)
-    gtk_container_add (GTK_CONTAINER (priv->empty_placeholder), placeholder);
-}
diff --git a/src/libide/gui/ide-frame.h b/src/libide/gui/ide-frame.h
index 8b4078e5a..8d8c6a570 100644
--- a/src/libide/gui/ide-frame.h
+++ b/src/libide/gui/ide-frame.h
@@ -24,8 +24,7 @@
 # error "Only <libide-gui.h> can be included directly."
 #endif
 
-#include <gtk/gtk.h>
-#include <libide-core.h>
+#include <libpanel.h>
 
 #include "ide-page.h"
 
@@ -33,55 +32,14 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_FRAME (ide_frame_get_type())
 
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeFrame, ide_frame, IDE, FRAME, GtkBox)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeFrame, ide_frame, IDE, FRAME, PanelFrame)
 
-struct _IdeFrameClass
-{
-  GtkBoxClass parent_class;
-
-  void     (*agree_to_close_async)  (IdeFrame             *stack,
-                                     GCancellable         *cancellable,
-                                     GAsyncReadyCallback   callback,
-                                     gpointer              user_data);
-  gboolean (*agree_to_close_finish) (IdeFrame             *stack,
-                                     GAsyncResult         *result,
-                                     GError              **error);
-
-  /*< private >*/
-  gpointer _reserved[16];
-};
-
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_frame_new                   (void);
-IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_frame_get_titlebar          (IdeFrame             *self);
-IDE_AVAILABLE_IN_3_32
-IdePage   *ide_frame_get_visible_child     (IdeFrame             *self);
-IDE_AVAILABLE_IN_3_32
-void       ide_frame_set_visible_child     (IdeFrame             *self,
-                                            IdePage              *page);
-IDE_AVAILABLE_IN_3_32
-gboolean   ide_frame_get_has_page          (IdeFrame             *self);
-IDE_AVAILABLE_IN_3_32
-void       ide_frame_agree_to_close_async  (IdeFrame             *self,
-                                            GCancellable         *cancellable,
-                                            GAsyncReadyCallback   callback,
-                                            gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
-gboolean   ide_frame_agree_to_close_finish (IdeFrame             *self,
-                                            GAsyncResult         *result,
-                                            GError              **error);
-IDE_AVAILABLE_IN_3_32
-void       ide_frame_foreach_page          (IdeFrame             *self,
-                                            GtkCallback           callback,
-                                            gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
-void       ide_frame_add_with_depth        (IdeFrame             *self,
-                                            GtkWidget            *widget,
-                                            guint                 position);
-IDE_AVAILABLE_IN_3_34
-void       ide_frame_set_placeholder       (IdeFrame             *self,
-                                            GtkWidget            *placeholder);
+IDE_AVAILABLE_IN_ALL
+GtkWidget *ide_frame_new          (void);
+IDE_AVAILABLE_IN_ALL
+void       ide_frame_foreach_page (IdeFrame        *self,
+                                   IdePageCallback  callback,
+                                   gpointer         user_data);
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-gui-private.h b/src/libide/gui/ide-gui-private.h
index df3ba4b94..a92a1c249 100644
--- a/src/libide/gui/ide-gui-private.h
+++ b/src/libide/gui/ide-gui-private.h
@@ -20,16 +20,15 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
+#include <adwaita.h>
 #include <libpeas/peas.h>
 #include <libpeas/peas-autocleanups.h>
+#include <libpanel.h>
 
 #include <libide-core.h>
 #include <libide-projects.h>
 
 #include "ide-command-manager.h"
-#include "ide-frame.h"
-#include "ide-frame-header.h"
 #include "ide-grid.h"
 #include "ide-grid-column.h"
 #include "ide-header-bar.h"
@@ -53,43 +52,6 @@ void      _ide_command_manager_execute          (IdeCommandManager   *self,
                                                  const gchar         *command);
 gboolean  _ide_gtk_widget_action_is_stateful    (GtkWidget           *widget,
                                                  const gchar         *action_name);
-void      _ide_frame_init_actions               (IdeFrame            *self);
-void      _ide_frame_init_shortcuts             (IdeFrame            *self);
-void      _ide_frame_update_actions             (IdeFrame            *self);
-void      _ide_frame_transfer                   (IdeFrame            *self,
-                                                 IdeFrame            *dest,
-                                                 IdePage             *view);
-void      _ide_grid_column_init_actions         (IdeGridColumn       *self);
-void      _ide_grid_column_update_actions       (IdeGridColumn       *self);
-gboolean  _ide_grid_column_is_empty             (IdeGridColumn       *self);
-void      _ide_grid_column_try_close            (IdeGridColumn       *self);
-IdeFrame *_ide_grid_get_nth_stack               (IdeGrid             *self,
-                                                 gint                 nth);
-IdeFrame *_ide_grid_get_nth_stack_for_column    (IdeGrid             *self,
-                                                 IdeGridColumn       *column,
-                                                 gint                 nth);
-void      _ide_grid_init_actions                (IdeGrid             *self);
-void      _ide_grid_stack_added                 (IdeGrid             *self,
-                                                 IdeFrame            *stack);
-void      _ide_grid_stack_removed               (IdeGrid             *self,
-                                                 IdeFrame            *stack);
-void      _ide_frame_request_close              (IdeFrame            *stack,
-                                                 IdePage             *view);
-void      _ide_frame_header_update              (IdeFrameHeader      *self,
-                                                 IdePage             *view);
-void      _ide_frame_header_focus_list          (IdeFrameHeader      *self);
-void      _ide_frame_header_hide                (IdeFrameHeader      *self);
-void      _ide_frame_header_popdown             (IdeFrameHeader      *self);
-void      _ide_frame_header_set_pages           (IdeFrameHeader      *self,
-                                                 GListModel          *model);
-void      _ide_frame_header_set_title           (IdeFrameHeader      *self,
-                                                 const gchar         *title);
-void      _ide_frame_header_set_modified        (IdeFrameHeader      *self,
-                                                 gboolean             modified);
-void      _ide_frame_header_set_background_rgba (IdeFrameHeader      *self,
-                                                 const GdkRGBA       *background_rgba);
-void      _ide_frame_header_set_foreground_rgba (IdeFrameHeader      *self,
-                                                 const GdkRGBA       *foreground_rgba);
 void      _ide_primary_workspace_init_actions   (IdePrimaryWorkspace *self);
 void      _ide_workspace_init_actions           (IdeWorkspace        *self);
 GList    *_ide_workspace_get_mru_link           (IdeWorkspace        *self);
diff --git a/src/libide/gui/ide-page.c b/src/libide/gui/ide-page.c
index dd083db23..b4791dbaa 100644
--- a/src/libide/gui/ide-page.c
+++ b/src/libide/gui/ide-page.c
@@ -69,7 +69,7 @@ enum {
   N_SIGNALS
 };
 
-G_DEFINE_TYPE_WITH_PRIVATE (IdePage, ide_page, GTK_TYPE_WIDGET)
+G_DEFINE_TYPE_WITH_PRIVATE (IdePage, ide_page, PANEL_TYPE_WIDGET)
 
 static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
@@ -110,23 +110,6 @@ find_focus_child (GtkWidget *widget,
     *handled = gtk_widget_child_focus (widget, GTK_DIR_TAB_FORWARD);
 }
 
-static void
-ide_page_grab_focus (GtkWidget *widget)
-{
-  gboolean handled = FALSE;
-
-  g_assert (IDE_IS_PAGE (widget));
-
-  /*
-   * This default grab_focus override just looks for the first child (generally
-   * something like a scrolled window) and tries to move forward on focusing
-   * the child widget. In most cases, this should work without intervention
-   * from the child subclass.
-   */
-
-  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) find_focus_child, &handled);
-}
-
 static void
 ide_page_hierarchy_changed (GtkWidget *widget,
                             GtkWidget *previous_toplevel)
@@ -297,7 +280,6 @@ ide_page_class_init (IdePageClass *klass)
   object_class->get_property = ide_page_get_property;
   object_class->set_property = ide_page_set_property;
 
-  widget_class->grab_focus = ide_page_grab_focus;
   widget_class->hierarchy_changed = ide_page_hierarchy_changed;
 
   klass->agree_to_close_async = ide_page_real_agree_to_close_async;
diff --git a/src/libide/gui/ide-page.h b/src/libide/gui/ide-page.h
index 0a9dfe5c1..6172fb7bb 100644
--- a/src/libide/gui/ide-page.h
+++ b/src/libide/gui/ide-page.h
@@ -24,7 +24,8 @@
 # error "Only <libide-gui.h> can be included directly."
 #endif
 
-#include <gtk/gtk.h>
+#include <libpanel.h>
+
 #include <libide-core.h>
 
 G_BEGIN_DECLS
@@ -32,14 +33,14 @@ G_BEGIN_DECLS
 #define IDE_TYPE_PAGE (ide_page_get_type())
 
 IDE_AVAILABLE_IN_ALL
-G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, GtkWidget)
+G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, PanelWidget)
 
 typedef void (*IdePageCallback) (IdePage  *page,
                                  gpointer  user_data);
 
 struct _IdePageClass
 {
-  GtkWidgetClass parent_class;
+  PanelWidgetClass parent_class;
 
   void           (*agree_to_close_async)  (IdePage              *self,
                                            GCancellable         *cancellable,
diff --git a/src/libide/gui/meson.build b/src/libide/gui/meson.build
index 733d65b85..0ea06b172 100644
--- a/src/libide/gui/meson.build
+++ b/src/libide/gui/meson.build
@@ -18,9 +18,6 @@ libide_gui_public_headers = [
   'ide-environment-editor.h',
   'ide-fancy-tree-view.h',
   'ide-frame-addin.h',
-  'ide-frame-empty-state.h',
-  'ide-frame-header.h',
-  'ide-frame.h',
   'ide-grid-column.h',
   'ide-grid.h',
   'ide-gui-global.h',
@@ -60,7 +57,6 @@ install_headers(libide_gui_public_headers, subdir: libide_gui_header_subdir)
 libide_gui_private_headers = [
   'ide-application-private.h',
   'ide-environment-editor-row.h',
-  'ide-frame-wrapper.h',
   'ide-gui-private.h',
   'ide-keybindings.h',
   'ide-notification-list-box-row-private.h',
@@ -82,9 +78,6 @@ libide_gui_private_sources = [
   'ide-application-shortcuts.c',
   'ide-application-plugins.c',
   'ide-environment-editor-row.c',
-  'ide-frame-actions.c',
-  'ide-frame-shortcuts.c',
-  'ide-frame-wrapper.c',
   'ide-grid-actions.c',
   'ide-grid-column-actions.c',
   'ide-header-bar-shortcuts.c',
@@ -116,8 +109,6 @@ libide_gui_public_sources = [
   'ide-environment-editor.c',
   'ide-fancy-tree-view.c',
   'ide-frame-addin.c',
-  'ide-frame-empty-state.c',
-  'ide-frame-header.c',
   'ide-frame.c',
   'ide-grid-column.c',
   'ide-grid.c',


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