[gnome-builder/wip/gtk4-port: 112/343] libide/gui: get IdeWorkbench to compile




commit 2f47d0dcc9534f40bd8f57bf41abe1299e78e2fe
Author: Christian Hergert <chergert redhat com>
Date:   Mon Mar 28 22:06:59 2022 -0700

    libide/gui: get IdeWorkbench to compile
    
    This also shuffles things around to make that possible, including the
    omnibar and various workspace stuff.

 src/libide/gui/ide-grid.c              |   2 +-
 src/libide/gui/ide-grid.h              |   6 -
 src/libide/gui/ide-gui-global.c        |  64 ++-----
 src/libide/gui/ide-gui-global.h        |  15 +-
 src/libide/gui/ide-gui-private.h       |  12 --
 src/libide/gui/ide-omni-bar.c          | 315 ++++-----------------------------
 src/libide/gui/ide-omni-bar.h          |  24 ++-
 src/libide/gui/ide-omni-bar.ui         | 171 ++++++------------
 src/libide/gui/ide-page.h              |   3 +
 src/libide/gui/ide-primary-workspace.h |   3 +-
 src/libide/gui/ide-workbench-private.h |  31 ++++
 src/libide/gui/ide-workbench.c         | 182 +++++++------------
 src/libide/gui/ide-workspace-private.h |  38 ++++
 src/libide/gui/ide-workspace.h         |  34 +---
 src/libide/gui/meson.build             |   1 +
 15 files changed, 270 insertions(+), 631 deletions(-)
---
diff --git a/src/libide/gui/ide-grid.c b/src/libide/gui/ide-grid.c
index a80b504fb..5f8f8f3ce 100644
--- a/src/libide/gui/ide-grid.c
+++ b/src/libide/gui/ide-grid.c
@@ -105,7 +105,7 @@ ide_grid_foreach_page (IdeGrid         *self,
   for (guint i = 0; i < pages->len; i++)
     {
       const PageInfo *info = &g_array_index (pages, PageInfo, i);
-      callback (info->page, info->column, info->row, info->depth, user_data);
+      callback (info->page, user_data);
     }
 }
 
diff --git a/src/libide/gui/ide-grid.h b/src/libide/gui/ide-grid.h
index ea0b4b3fa..e15a86d4d 100644
--- a/src/libide/gui/ide-grid.h
+++ b/src/libide/gui/ide-grid.h
@@ -34,12 +34,6 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_GRID (ide_grid_get_type())
 
-typedef void (*IdePageCallback) (IdePage  *page,
-                                 guint     column,
-                                 guint     row,
-                                 guint     depth,
-                                 gpointer  user_data);
-
 IDE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (IdeGrid, ide_grid, IDE, GRID, PanelGrid)
 
diff --git a/src/libide/gui/ide-gui-global.c b/src/libide/gui/ide-gui-global.c
index c56b82aca..691d2b3e9 100644
--- a/src/libide/gui/ide-gui-global.c
+++ b/src/libide/gui/ide-gui-global.c
@@ -25,7 +25,6 @@
 #include <libide-threading.h>
 
 #include "ide-gui-global.h"
-#include "ide-gui-private.h"
 #include "ide-workspace.h"
 
 static GQuark quark_handler;
@@ -81,24 +80,28 @@ has_context_property (GtkWidget *widget)
   g_assert (GTK_IS_WIDGET (widget));
 
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (widget), "context");
-  return pspec != NULL && g_type_is_a (pspec->value_type, IDE_TYPE_CONTEXT);
+
+  return pspec != NULL &&
+         (pspec->flags & G_PARAM_READABLE) != 0 &&
+         g_type_is_a (pspec->value_type, IDE_TYPE_CONTEXT);
 }
 
 static void
-ide_widget_hierarchy_changed (GtkWidget *widget,
-                              GtkWidget *previous_toplevel,
-                              gpointer   user_data)
+ide_widget_hierarchy_changed (GtkWidget  *widget,
+                              GParamSpec *pspec,
+                              gpointer    user_data)
 {
   GtkWidget *toplevel;
 
   g_assert (GTK_IS_WIDGET (widget));
+  g_assert (user_data == NULL);
 
   if (GTK_IS_WINDOW (previous_toplevel))
     g_signal_handlers_disconnect_by_func (previous_toplevel,
                                           G_CALLBACK (ide_widget_notify_context),
                                           widget);
 
-  toplevel = gtk_widget_get_toplevel (widget);
+  toplevel = GTK_WIDGET (gtk_widget_get_native (widget));
 
   if (GTK_IS_WINDOW (toplevel) && has_context_property (toplevel))
     {
@@ -117,8 +120,6 @@ ide_widget_hierarchy_changed (GtkWidget *widget,
  * @handler: (scope async): A callback to handle the context
  *
  * Calls @handler when the #IdeContext has been set for @widget.
- *
- * Since: 3.32
  */
 void
 ide_widget_set_context_handler (gpointer                widget,
@@ -138,11 +139,11 @@ ide_widget_set_context_handler (gpointer                widget,
   g_object_set_qdata (G_OBJECT (widget), quark_handler, handler);
 
   g_signal_connect (widget,
-                    "hierarchy-changed",
+                    "notify::root"
                     G_CALLBACK (ide_widget_hierarchy_changed),
                     NULL);
 
-  toplevel = gtk_widget_get_toplevel (widget);
+  toplevel = GTK_WIDGET (gtk_widget_get_native (widget));
 
   if (GTK_IS_WINDOW (toplevel))
     ide_widget_hierarchy_changed (widget, NULL, NULL);
@@ -155,8 +156,6 @@ ide_widget_set_context_handler (gpointer                widget,
  * Gets the context for the widget.
  *
  * Returns: (nullable) (transfer none): an #IdeContext, or %NULL
- *
- * Since: 3.32
  */
 IdeContext *
 ide_widget_get_context (GtkWidget *widget)
@@ -180,8 +179,6 @@ ide_widget_get_context (GtkWidget *widget)
  * Gets the #IdeWorkbench that contains @widget.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkbench or %NULL
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_widget_get_workbench (GtkWidget *widget)
@@ -190,7 +187,7 @@ ide_widget_get_workbench (GtkWidget *widget)
 
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
 
-  toplevel = gtk_widget_get_toplevel (widget);
+  toplevel = GTK_WIDGET (gtk_widget_get_native (widget));
 
   if (GTK_IS_WINDOW (toplevel))
     {
@@ -210,46 +207,11 @@ ide_widget_get_workbench (GtkWidget *widget)
  * Gets the #IdeWorkspace containing @widget.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
- *
- * Since: 3.32
  */
 IdeWorkspace *
 ide_widget_get_workspace (GtkWidget *widget)
 {
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
 
-  return IDE_WORKSPACE (gtk_widget_get_root (widget));
-}
-
-static void
-show_parents (GtkWidget *widget)
-{
-  GtkWidget *workspace;
-  GtkWidget *parent;
-
-  g_assert (GTK_IS_WIDGET (widget));
-
-  workspace = gtk_widget_get_ancestor (widget, IDE_TYPE_WORKSPACE);
-  parent = gtk_widget_get_parent (widget);
-
-  if (DZL_IS_DOCK_REVEALER (widget))
-    dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (widget), TRUE);
-
-  if (IDE_IS_SURFACE (widget))
-    ide_workspace_set_visible_surface (IDE_WORKSPACE (workspace), IDE_SURFACE (widget));
-
-  if (GTK_IS_STACK (parent))
-    gtk_stack_set_visible_child (GTK_STACK (parent), widget);
-
-  if (parent != NULL)
-    show_parents (parent);
-}
-
-void
-ide_widget_reveal_and_grab (GtkWidget *widget)
-{
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-
-  show_parents (widget);
-  gtk_widget_grab_focus (widget);
+  return IDE_WORKSPACE (gtk_widget_get_native (widget));
 }
diff --git a/src/libide/gui/ide-gui-global.h b/src/libide/gui/ide-gui-global.h
index 99b01b5da..1fc5ee5ef 100644
--- a/src/libide/gui/ide-gui-global.h
+++ b/src/libide/gui/ide-gui-global.h
@@ -20,7 +20,12 @@
 
 #pragma once
 
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
 #include <gtk/gtk.h>
+
 #include <libide-core.h>
 
 #include "ide-workbench.h"
@@ -44,16 +49,14 @@ G_BEGIN_DECLS
 typedef void (*IdeWidgetContextHandler) (GtkWidget  *widget,
                                          IdeContext *context);
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void          ide_widget_set_context_handler (gpointer                  widget,
                                               IdeWidgetContextHandler   handler);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeContext   *ide_widget_get_context         (GtkWidget                *widget);
-IDE_AVAILABLE_IN_3_32
-void          ide_widget_reveal_and_grab     (GtkWidget                *widget);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeWorkbench *ide_widget_get_workbench       (GtkWidget                *widget);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeWorkspace *ide_widget_get_workspace       (GtkWidget                *widget);
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-gui-private.h b/src/libide/gui/ide-gui-private.h
index f423dd8f6..fc1e1922e 100644
--- a/src/libide/gui/ide-gui-private.h
+++ b/src/libide/gui/ide-gui-private.h
@@ -40,18 +40,6 @@
 G_BEGIN_DECLS
 
 void      _ide_primary_workspace_init_actions   (IdePrimaryWorkspace *self);
-void      _ide_workspace_init_actions           (IdeWorkspace        *self);
-GList    *_ide_workspace_get_mru_link           (IdeWorkspace        *self);
-void      _ide_workspace_add_page_mru           (IdeWorkspace        *self,
-                                                 GList               *mru_link);
-void      _ide_workspace_remove_page_mru        (IdeWorkspace        *self,
-                                                 GList               *mru_link);
-void      _ide_workspace_move_front_page_mru    (IdeWorkspace        *workspace,
-                                                 GList               *mru_link);
-void      _ide_workspace_set_context            (IdeWorkspace        *workspace,
-                                                 IdeContext          *context);
-gboolean  _ide_workbench_is_last_workspace      (IdeWorkbench        *self,
-                                                 IdeWorkspace        *workspace);
 void      _ide_header_bar_init_shortcuts        (IdeHeaderBar        *self);
 void      _ide_header_bar_show_menu             (IdeHeaderBar        *self);
 void      _ide_surface_set_fullscreen           (IdeSurface          *self,
diff --git a/src/libide/gui/ide-omni-bar.c b/src/libide/gui/ide-omni-bar.c
index 0ec359893..6633c4982 100644
--- a/src/libide/gui/ide-omni-bar.c
+++ b/src/libide/gui/ide-omni-bar.c
@@ -23,10 +23,8 @@
 #include "config.h"
 
 #include <libpeas/peas.h>
-#include <dazzle.h>
 
 #include "ide-gui-global.h"
-#include "ide-gui-private.h"
 #include "ide-notification-list-box-row-private.h"
 #include "ide-notification-stack-private.h"
 #include "ide-omni-bar-addin.h"
@@ -34,25 +32,16 @@
 
 struct _IdeOmniBar
 {
-  AdwBin                parent_instance;
+  PanelOmniBar          parent_instance;
 
   PeasExtensionSet     *addins;
-  GtkGesture           *gesture;
-  GtkEventController   *motion;
 
-  GtkEventBox          *entry_event_box;
-  GtkStack             *top_stack;
+  GtkStack             *stack;
   GtkPopover           *popover;
-  DzlEntryBox          *entry_box;
   IdeNotificationStack *notification_stack;
   GtkListBox           *notifications_list_box;
-  DzlPriorityBox       *inner_box;
-  DzlPriorityBox       *outer_box;
-  GtkProgressBar       *progress;
   GtkWidget            *placeholder;
-  DzlPriorityBox       *sections_box;
-
-  guint                 in_button : 1;
+  GtkBox               *sections_box;
 };
 
 static void ide_omni_bar_move_next     (IdeOmniBar        *self,
@@ -61,108 +50,17 @@ static void ide_omni_bar_move_previous (IdeOmniBar        *self,
                                         GVariant          *param);
 static void buildable_iface_init       (GtkBuildableIface *iface);
 
-DZL_DEFINE_ACTION_GROUP (IdeOmniBar, ide_omni_bar, {
+IDE_DEFINE_ACTION_GROUP (IdeOmniBar, ide_omni_bar, {
   { "move-next", ide_omni_bar_move_next },
   { "move-previous", ide_omni_bar_move_previous },
 })
 
-G_DEFINE_FINAL_TYPE_WITH_CODE (IdeOmniBar, ide_omni_bar, ADW_TYPE_BIN,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, ide_omni_bar_init_action_group)
-                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeOmniBar, ide_omni_bar, PANEL_TYPE_OMNI_BAR,
+                               G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, ide_omni_bar_init_action_group)
+                               G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
 
 static GtkBuildableIface *parent_buildable_iface;
 
-static void
-ide_omni_bar_popover_closed_cb (IdeOmniBar *self,
-                                GtkPopover *popover)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_POPOVER (popover));
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-
-  state_flags &= ~GTK_STATE_FLAG_ACTIVE;
-  state_flags &= ~GTK_STATE_FLAG_PRELIGHT;
-
-  gtk_style_context_set_state (style_context, state_flags);
-}
-
-static void
-multipress_pressed_cb (IdeOmniBar           *self,
-                       guint                 n_press,
-                       gdouble               x,
-                       gdouble               y,
-                       GtkGestureMultiPress *gesture)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_GESTURE_MULTI_PRESS (gesture));
-
-  if (gtk_widget_get_focus_on_click (GTK_WIDGET (self)) &&
-      !gtk_widget_has_focus (GTK_WIDGET (self)))
-    gtk_widget_grab_focus (GTK_WIDGET (self));
-
-  self->in_button = TRUE;
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-  gtk_style_context_set_state (style_context, state_flags | GTK_STATE_FLAG_ACTIVE);
-
-  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-}
-
-static void
-multipress_released_cb (IdeOmniBar           *self,
-                        guint                 n_press,
-                        gdouble               x,
-                        gdouble               y,
-                        GtkGestureMultiPress *gesture)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-  gboolean show;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_GESTURE_MULTI_PRESS (gesture));
-
-  show = self->in_button;
-  self->in_button = FALSE;
-
-  if (show)
-    {
-      gtk_popover_popup (self->popover);
-      return;
-    }
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-  gtk_style_context_set_state (style_context, state_flags & ~GTK_STATE_FLAG_ACTIVE);
-}
-
-static void
-multipress_cancel_cb (IdeOmniBar           *self,
-                      GdkEventSequence     *sequence,
-                      GtkGestureMultiPress *gesture)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_GESTURE_MULTI_PRESS (gesture));
-
-  self->in_button = FALSE;
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-  gtk_style_context_set_state (style_context, state_flags & ~GTK_STATE_FLAG_ACTIVE);
-}
-
 static void
 ide_omni_bar_notification_stack_changed_cb (IdeOmniBar           *self,
                                             IdeNotificationStack *stack)
@@ -178,23 +76,21 @@ ide_omni_bar_notification_stack_changed_cb (IdeOmniBar           *self,
   ide_omni_bar_set_action_enabled (self, "move-previous", enabled);
   ide_omni_bar_set_action_enabled (self, "move-next", enabled);
 
-  _ide_gtk_progress_bar_stop_pulsing (self->progress);
-  gtk_widget_hide (GTK_WIDGET (self->progress));
+  panel_omni_bar_stop_pulsing (PANEL_OMNI_BAR (self));
 
   if ((notif = ide_notification_stack_get_visible (stack)))
     {
       if (ide_notification_get_has_progress (notif))
         {
           if (ide_notification_get_progress_is_imprecise (notif))
-            _ide_gtk_progress_bar_start_pulsing (self->progress);
-          gtk_widget_show (GTK_WIDGET (self->progress));
+            panel_omni_bar_start_pulsing (PANEL_OMNI_BAR (self));
         }
     }
 
   if (ide_notification_stack_is_empty (stack))
-    gtk_stack_set_visible_child_name (self->top_stack, "placeholder");
+    gtk_stack_set_visible_child_name (self->stack, "placeholder");
   else
-    gtk_stack_set_visible_child_name (self->top_stack, "notifications");
+    gtk_stack_set_visible_child_name (self->stack, "notifications");
 }
 
 static void
@@ -250,10 +146,10 @@ create_notification_row (gpointer item,
 }
 
 static gboolean
-filter_for_popover (GObject  *object,
-                    gpointer  user_data)
+filter_for_popover (gpointer item,
+                    gpointer user_data)
 {
-  IdeNotification *notif = (IdeNotification *)object;
+  IdeNotification *notif = item;
 
   g_assert (IDE_IS_NOTIFICATION (notif));
   g_assert (user_data == NULL);
@@ -268,7 +164,8 @@ ide_omni_bar_context_set_cb (GtkWidget  *widget,
 {
   IdeOmniBar *self = (IdeOmniBar *)widget;
   g_autoptr(IdeObject) notifications = NULL;
-  g_autoptr(DzlListModelFilter) filter = NULL;
+  g_autoptr(GtkFilterListModel) model = NULL;
+  g_autoptr(GtkCustomFilter) filter = NULL;
 
   g_assert (IDE_IS_OMNI_BAR (self));
   g_assert (IDE_IS_CONTEXT (context));
@@ -277,8 +174,9 @@ ide_omni_bar_context_set_cb (GtkWidget  *widget,
   notifications = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS);
   ide_notification_stack_bind_model (self->notification_stack, G_LIST_MODEL (notifications));
 
-  filter = dzl_list_model_filter_new (G_LIST_MODEL (notifications));
-  dzl_list_model_filter_set_filter_func (filter, filter_for_popover, NULL, NULL);
+  filter = gtk_custom_filter_new (filter_for_popover, NULL, NULL);
+  model = gtk_filter_list_model_new (g_object_ref (G_LIST_MODEL (notifications)),
+                                     GTK_FILTER (g_steal_pointer (&filter)));
   gtk_list_box_bind_model (self->notifications_list_box,
                            G_LIST_MODEL (filter),
                            create_notification_row,
@@ -302,61 +200,6 @@ ide_omni_bar_context_set_cb (GtkWidget  *widget,
                               self);
 }
 
-static void
-ide_omni_bar_motion_enter_cb (IdeOmniBar               *self,
-                              gdouble                   x,
-                              gdouble                   y,
-                              GtkEventControllerMotion *motion)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-
-  if ((state_flags & GTK_STATE_FLAG_PRELIGHT) == 0)
-    gtk_style_context_set_state (style_context, state_flags | GTK_STATE_FLAG_PRELIGHT);
-}
-
-static void
-ide_omni_bar_motion_leave_cb (IdeOmniBar               *self,
-                              GtkEventControllerMotion *motion)
-{
-  GtkStyleContext *style_context;
-  GtkStateFlags state_flags;
-
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
-
-  style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  state_flags = gtk_style_context_get_state (style_context);
-
-  if (state_flags & GTK_STATE_FLAG_PRELIGHT)
-    gtk_style_context_set_state (style_context, state_flags & ~GTK_STATE_FLAG_PRELIGHT);
-}
-
-static void
-ide_omni_bar_motion_cb (IdeOmniBar               *self,
-                        gdouble                   x,
-                        gdouble                   y,
-                        GtkEventControllerMotion *motion)
-{
-  g_assert (IDE_IS_OMNI_BAR (self));
-  g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
-
-  /*
-   * Because of how crossing-events work with Gtk 3, we don't get reliable
-   * crossing events for the motion controller. So every motion (which we do
-   * seem to get semi-reliably), just re-run the enter-notify path to ensure
-   * we get proper state set.
-   */
-
-  ide_omni_bar_motion_enter_cb (self, x, y, motion);
-}
-
 static gboolean
 ide_omni_bar_query_tooltip (GtkWidget  *widget,
                             gint        x,
@@ -400,65 +243,40 @@ ide_omni_bar_notification_row_activated (IdeOmniBar                *self,
   notif = ide_notification_list_box_row_get_notification (row);
 
   if (ide_notification_get_default_action (notif, &default_action, &default_target))
-    {
-      gchar *name = strchr (default_action, '.');
-      gchar *group = default_action;
-
-      if (name != NULL)
-        {
-          *name = '\0';
-          name++;
-        }
-      else
-        {
-          group = NULL;
-          name = default_action;
-        }
-
-      dzl_gtk_widget_action (GTK_WIDGET (list_box), group, name, default_target);
-    }
+    gtk_widget_activate_action_variant (GTK_WIDGET (list_box), default_action, default_target);
 }
 
 static void
-ide_omni_bar_destroy (GtkWidget *widget)
+ide_omni_bar_dispose (GObject *object)
 {
-  IdeOmniBar *self = (IdeOmniBar *)widget;
+  IdeOmniBar *self = (IdeOmniBar *)object;
 
   g_assert (IDE_IS_OMNI_BAR (self));
 
-  if (self->progress != NULL)
-    _ide_gtk_progress_bar_stop_pulsing (self->progress);
-
   g_clear_object (&self->addins);
-  g_clear_object (&self->gesture);
-  g_clear_object (&self->motion);
 
-  GTK_WIDGET_CLASS (ide_omni_bar_parent_class)->destroy (widget);
+  G_OBJECT_CLASS (ide_omni_bar_parent_class)->dispose (object);
 }
 
 static void
 ide_omni_bar_class_init (IdeOmniBarClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  widget_class->destroy = ide_omni_bar_destroy;
+  object_class->dispose = ide_omni_bar_dispose;
+
   widget_class->query_tooltip = ide_omni_bar_query_tooltip;
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-omni-bar.ui");
-  gtk_widget_class_set_css_name (widget_class, "omnibar");
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, entry_box);
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, entry_event_box);
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, inner_box);
   gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, notification_stack);
   gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, notifications_list_box);
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, outer_box);
   gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, popover);
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, progress);
   gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, sections_box);
-  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, top_stack);
+  gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, stack);
   gtk_widget_class_bind_template_callback (widget_class, ide_omni_bar_notification_row_activated);
+  gtk_widget_class_set_css_name (widget_class, "omnibar");
 
-  g_type_ensure (DZL_TYPE_ENTRY_BOX);
   g_type_ensure (IDE_TYPE_NOTIFICATION_STACK);
 }
 
@@ -469,61 +287,12 @@ ide_omni_bar_init (IdeOmniBar *self)
 
   gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE);
 
-  gtk_widget_add_events (GTK_WIDGET (self),
-                         (GDK_POINTER_MOTION_MASK |
-                          GDK_ENTER_NOTIFY_MASK |
-                          GDK_LEAVE_NOTIFY_MASK));
-
-  self->motion = gtk_event_controller_motion_new (GTK_WIDGET (self));
-  gtk_event_controller_set_propagation_phase (self->motion, GTK_PHASE_CAPTURE);
-
-  g_signal_connect_swapped (self->motion,
-                            "enter",
-                            G_CALLBACK (ide_omni_bar_motion_enter_cb),
-                            self);
-
-  g_signal_connect_swapped (self->motion,
-                            "motion",
-                            G_CALLBACK (ide_omni_bar_motion_cb),
-                            self);
-
-  g_signal_connect_swapped (self->motion,
-                            "leave",
-                            G_CALLBACK (ide_omni_bar_motion_leave_cb),
-                            self);
-
-  self->gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self->entry_event_box));
-  gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->gesture), FALSE);
-  gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (self->gesture), TRUE);
-  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->gesture), GDK_BUTTON_PRIMARY);
-  g_signal_connect_object (self->gesture,
-                           "pressed",
-                           G_CALLBACK (multipress_pressed_cb),
-                           self,
-                           G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->gesture,
-                           "released",
-                           G_CALLBACK (multipress_released_cb),
-                           self,
-                           G_CONNECT_SWAPPED);
-  g_signal_connect_object (self->gesture,
-                           "cancel",
-                           G_CALLBACK (multipress_cancel_cb),
-                           self,
-                           G_CONNECT_SWAPPED);
-
   g_signal_connect_object (self->notification_stack,
                            "changed",
                            G_CALLBACK (ide_omni_bar_notification_stack_changed_cb),
                            self,
                            G_CONNECT_SWAPPED);
 
-  g_signal_connect_object (self->popover,
-                           "closed",
-                           G_CALLBACK (ide_omni_bar_popover_closed_cb),
-                           self,
-                           G_CONNECT_SWAPPED);
-
   gtk_widget_insert_action_group (GTK_WIDGET (self), "omnibar", G_ACTION_GROUP (self));
 
   ide_omni_bar_set_action_enabled (self, "move-previous", FALSE);
@@ -611,28 +380,22 @@ ide_omni_bar_set_placeholder (IdeOmniBar *self,
     return;
 
   if (self->placeholder)
-    gtk_widget_destroy (self->placeholder);
+    gtk_stack_remove (self->stack, self->placeholder);
 
   self->placeholder = widget;
 
   if (self->placeholder)
     {
-      g_signal_connect (self->placeholder,
-                        "destroy",
-                        G_CALLBACK (gtk_widget_destroyed),
-                        self->placeholder);
-      gtk_container_add_with_properties (GTK_CONTAINER (self->top_stack), self->placeholder,
-                                         "name", "placeholder",
-                                         NULL);
+      gtk_stack_add_child_named (self->stack, self->placeholder, "placeholder");
       if (self->notification_stack == NULL ||
           ide_notification_stack_is_empty (self->notification_stack))
-        gtk_stack_set_visible_child_name (self->top_stack, "placeholder");
+        gtk_stack_set_visible_child_name (self->stack, "placeholder");
     }
 }
 
 static void
 ide_omni_bar_add_child (GtkBuildable *buildable,
-                          GtkBuilder   *builder,
+                        GtkBuilder   *builder,
                         GObject      *child,
                         const gchar  *type)
 {
@@ -642,17 +405,7 @@ ide_omni_bar_add_child (GtkBuildable *buildable,
   g_assert (GTK_IS_BUILDER (builder));
   g_assert (G_IS_OBJECT (child));
 
-  if (ide_str_equal0 (type, "start") && GTK_IS_WIDGET (child))
-    ide_omni_bar_add_button (IDE_OMNI_BAR (self),
-                             GTK_WIDGET (child),
-                             GTK_PACK_START,
-                             0);
-  else if (ide_str_equal0 (type, "end") && GTK_IS_WIDGET (child))
-    ide_omni_bar_add_button (IDE_OMNI_BAR (self),
-                             GTK_WIDGET (child),
-                             GTK_PACK_END,
-                             0);
-  else if (ide_str_equal0 (type, "placeholder") && GTK_IS_WIDGET (child))
+  if (ide_str_equal0 (type, "placeholder") && GTK_IS_WIDGET (child))
     ide_omni_bar_set_placeholder (IDE_OMNI_BAR (self), GTK_WIDGET (child));
   else
     parent_buildable_iface->add_child (buildable, builder, child, type);
@@ -672,8 +425,6 @@ buildable_iface_init (GtkBuildableIface *iface)
  * @priority: sort priority for the section
  *
  * Adds @widget to the omnibar popover, sorted by @priority
- *
- * Since: 3.32
  */
 void
 ide_omni_bar_add_popover_section (IdeOmniBar *self,
diff --git a/src/libide/gui/ide-omni-bar.h b/src/libide/gui/ide-omni-bar.h
index 267564cc0..5851fe2d8 100644
--- a/src/libide/gui/ide-omni-bar.h
+++ b/src/libide/gui/ide-omni-bar.h
@@ -24,33 +24,29 @@
 # error "Only <libide-gui.h> can be included directly."
 #endif
 
-#include <gtk/gtk.h>
+#include <libpanel.h>
+
 #include <libide-core.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_OMNI_BAR (ide_omni_bar_get_type())
 
-IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeOmniBar, ide_omni_bar, IDE, OMNI_BAR, AdwBin)
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeOmniBar, ide_omni_bar, IDE, OMNI_BAR, PanelOmniBar)
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 GtkWidget *ide_omni_bar_new                 (void);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void       ide_omni_bar_add_status_icon     (IdeOmniBar  *self,
                                              GtkWidget   *widget,
-                                             gint         priority);
-IDE_AVAILABLE_IN_3_32
-void       ide_omni_bar_add_button          (IdeOmniBar  *self,
-                                             GtkWidget   *widget,
-                                             GtkPackType  pack_type,
-                                             gint         priority);
-IDE_AVAILABLE_IN_3_32
+                                             int          priority);
+IDE_AVAILABLE_IN_ALL
 void       ide_omni_bar_set_placeholder     (IdeOmniBar  *self,
                                              GtkWidget   *placeholder);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void       ide_omni_bar_add_popover_section (IdeOmniBar  *self,
                                              GtkWidget   *widget,
-                                             gint         priority);
+                                             int          priority);
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-omni-bar.ui b/src/libide/gui/ide-omni-bar.ui
index b4d9584e9..cd9ed03a3 100644
--- a/src/libide/gui/ide-omni-bar.ui
+++ b/src/libide/gui/ide-omni-bar.ui
@@ -1,133 +1,76 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="IdeOmniBar" parent="GtkBin">
-    <child>
-      <object class="DzlPriorityBox" id="outer_box">
-        <property name="hexpand">true</property>
-        <property name="visible">true</property>
-        <style>
-          <class name="linked"/>
-        </style>
-        <child type="center">
-          <object class="DzlEntryBox" id="entry_box">
-            <property name="max-width-chars">40</property>
-            <property name="visible">true</property>
+  <requires lib="gtk" version="4.0"/>
+  <requires lib="Adw" version="1.0"/>
+  <requires lib="Panel" version="1.0"/>
+  <template class="IdeOmniBar" parent="PanelOmniBar">
+    <property name="popover">
+      <object class="GtkPopover" id="popover">
+        <property name="width-request">500</property>
+        <property name="position">top</property>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            </child>
             <child>
-              <object class="GtkOverlay" id="overlay">
-                <property name="visible">true</property>
-                <child type="overlay">
-                  <object class="GtkProgressBar" id="progress">
-                    <property name="valign">end</property>
-                    <property name="hexpand">true</property>
-                    <property name="fraction" bind-source="notification_stack" bind-property="progress"/>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="osd"/>
-                    </style>
-                  </object>
-                </child>
-                <child>
-                  <object class="DzlPriorityBox" id="inner_box">
-                    <property name="margin-top">1</property>
-                    <property name="spacing">3</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="GtkBox">
-                        <property name="hexpand">false</property>
-                        <property name="vexpand">false</property>
-                        <property name="valign">center</property>
-                        <property name="orientation">vertical</property>
-                        <property name="visible">true</property>
-                        <style>
-                          <class name="pan"/>
-                        </style>
-                        <child>
-                          <object class="GtkButton">
-                            <property name="action-name">omnibar.move-previous</property>
-                            <property name="visible">true</property>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon-name">pan-up-symbolic</property>
-                                <property name="pixel-size">12</property>
-                                <property name="visible">true</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton">
-                            <property name="action-name">omnibar.move-next</property>
-                            <property name="visible">true</property>
-                            <child>
-                              <object class="GtkImage">
-                                <property name="icon-name">pan-down-symbolic</property>
-                                <property name="pixel-size">12</property>
-                                <property name="visible">true</property>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkEventBox" id="entry_event_box">
-                        <property name="visible">true</property>
-                        <child>
-                          <object class="GtkStack" id="top_stack">
-                            <property name="margin-start">3</property>
-                            <property name="margin-end">3</property>
-                            <property name="hexpand">true</property>
-                            <property name="visible">true</property>
-                            <child>
-                              <object class="IdeNotificationStack" id="notification_stack">
-                                <property name="visible">true</property>
-                              </object>
-                              <packing>
-                                <property name="name">notifications</property>
-                              </packing>
-                            </child>
-                          </object>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="expand">true</property>
-                      </packing>
-                    </child>
-                  </object>
-                </child>
+              <object class="GtkBox" id="sections_box">
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBox" id="notifications_list_box">
+                <signal name="row-activated" swapped="true" object="IdeOmniBar" 
handler="ide_omni_bar_notification_row_activated"/>
+                <property name="selection-mode">none</property>
               </object>
             </child>
           </object>
         </child>
       </object>
-    </child>
-  </template>
-  <object class="GtkPopover" id="popover">
-    <property name="width-request">500</property>
-    <property name="relative-to">IdeOmniBar</property>
-    <property name="position">top</property>
-    <style>
-      <class name="omnibar"/>
-    </style>
-    <child>
+    </property>
+    <child type="prefix">
       <object class="GtkBox">
+        <property name="hexpand">false</property>
+        <property name="vexpand">false</property>
+        <property name="valign">center</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">12</property>
-        <property name="visible">true</property>
+        <style>
+          <class name="pan"/>
+        </style>
         <child>
-          <object class="DzlPriorityBox" id="sections_box">
-            <property name="orientation">vertical</property>
-            <property name="visible">true</property>
+          <object class="GtkButton">
+            <property name="action-name">omnibar.move-previous</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">pan-up-symbolic</property>
+                <property name="pixel-size">12</property>
+              </object>
+            </child>
           </object>
         </child>
         <child>
-          <object class="GtkListBox" id="notifications_list_box">
-            <signal name="row-activated" swapped="true" object="IdeOmniBar" 
handler="ide_omni_bar_notification_row_activated"/>
-            <property name="selection-mode">none</property>
-            <property name="visible">true</property>
+          <object class="GtkButton">
+            <property name="action-name">omnibar.move-next</property>
+            <child>
+              <object class="GtkImage">
+                <property name="icon-name">pan-down-symbolic</property>
+                <property name="pixel-size">12</property>
+              </object>
+            </child>
           </object>
         </child>
       </object>
     </child>
-  </object>
+    <child type="prefix">
+      <object class="GtkStack" id="stack">
+        <property name="margin-start">3</property>
+        <property name="margin-end">3</property>
+        <property name="hexpand">true</property>
+        <child>
+          <object class="IdeNotificationStack" id="notification_stack"/>
+        </child>
+      </object>
+    </child>
+  </template>
 </interface>
diff --git a/src/libide/gui/ide-page.h b/src/libide/gui/ide-page.h
index 939e7d4d0..6172fb7bb 100644
--- a/src/libide/gui/ide-page.h
+++ b/src/libide/gui/ide-page.h
@@ -35,6 +35,9 @@ G_BEGIN_DECLS
 IDE_AVAILABLE_IN_ALL
 G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, PanelWidget)
 
+typedef void (*IdePageCallback) (IdePage  *page,
+                                 gpointer  user_data);
+
 struct _IdePageClass
 {
   PanelWidgetClass parent_class;
diff --git a/src/libide/gui/ide-primary-workspace.h b/src/libide/gui/ide-primary-workspace.h
index a20da72eb..3c9be2113 100644
--- a/src/libide/gui/ide-primary-workspace.h
+++ b/src/libide/gui/ide-primary-workspace.h
@@ -25,14 +25,13 @@
 #endif
 
 #include "ide-application.h"
-#include "ide-surface.h"
 #include "ide-workspace.h"
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_PRIMARY_WORKSPACE (ide_primary_workspace_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (IdePrimaryWorkspace, ide_primary_workspace, IDE, PRIMARY_WORKSPACE, IdeWorkspace)
 
 G_END_DECLS
diff --git a/src/libide/gui/ide-workbench-private.h b/src/libide/gui/ide-workbench-private.h
new file mode 100644
index 000000000..68bc88638
--- /dev/null
+++ b/src/libide/gui/ide-workbench-private.h
@@ -0,0 +1,31 @@
+/* ide-workbench-private.h
+ *
+ * Copyright 2022 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-workbench.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+gboolean _ide_workbench_is_last_workspace (IdeWorkbench *self,
+                                           IdeWorkspace *workspace);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workbench.c b/src/libide/gui/ide-workbench.c
index 62d3a9e52..7e8dbdef4 100644
--- a/src/libide/gui/ide-workbench.c
+++ b/src/libide/gui/ide-workbench.c
@@ -27,6 +27,7 @@
 
 #include <libide-commands.h>
 #include <libide-debugger.h>
+#include <libide-gtk.h>
 #include <libide-threading.h>
 
 #include "ide-build-private.h"
@@ -37,11 +38,10 @@
 
 #include "ide-application.h"
 #include "ide-gui-global.h"
-#include "ide-gui-private.h"
 #include "ide-primary-workspace.h"
-#include "ide-workbench.h"
 #include "ide-workbench-addin.h"
-#include "ide-workspace.h"
+#include "ide-workbench-private.h"
+#include "ide-workspace-private.h"
 
 /**
  * SECTION:ide-workbench
@@ -55,8 +55,6 @@
  * Usually, windows within the #IdeWorkbench are an #IdeWorkspace. They can
  * react to changes in the #IdeContext or its descendants to represent the
  * project and it's state.
- *
- * Since: 3.32
  */
 
 struct _IdeWorkbench
@@ -127,7 +125,7 @@ static void ide_workbench_action_reload_all  (IdeWorkbench *self,
                                               GVariant     *param);
 
 
-DZL_DEFINE_ACTION_GROUP (IdeWorkbench, ide_workbench, {
+IDE_DEFINE_ACTION_GROUP (IdeWorkbench, ide_workbench, {
   { "close", ide_workbench_action_close },
   { "open", ide_workbench_action_open },
   { "reload-files", ide_workbench_action_reload_all },
@@ -183,8 +181,6 @@ ignore_error (GError *error)
  * Helper to get the #IdeWorkbench for a given context.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkbench or %NULL
- *
- * Since: 3.40
  */
 IdeWorkbench *
 ide_workbench_from_context (IdeContext *context)
@@ -272,7 +268,7 @@ ide_workbench_addin_added_cb (PeasExtensionSet *set,
     ide_workbench_addin_load_project_async (addin, self->project_info, NULL, NULL, NULL);
 
   ide_workbench_foreach_workspace (self,
-                                   (GtkCallback)ide_workbench_addin_added_workspace_cb,
+                                   (IdeWorkspaceCallback)ide_workbench_addin_added_workspace_cb,
                                    addin);
 }
 
@@ -294,7 +290,7 @@ ide_workbench_addin_removed_cb (PeasExtensionSet *set,
    * track them for cleanup.
    */
   ide_workbench_foreach_workspace (self,
-                                   (GtkCallback)ide_workbench_addin_removed_workspace_cb,
+                                   (IdeWorkspaceCallback)ide_workbench_addin_removed_workspace_cb,
                                    addin);
 
   ide_workbench_addin_unload (addin, self);
@@ -315,7 +311,7 @@ ide_workbench_notify_context_title (IdeWorkbench *self,
   title = ide_context_dup_title (context);
   formatted = g_strdup_printf (_("Builder — %s"), title);
   ide_workbench_foreach_workspace (self,
-                                   (GtkCallback)gtk_window_set_title,
+                                   (IdeWorkspaceCallback)gtk_window_set_title,
                                    formatted);
 }
 
@@ -463,8 +459,6 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
    *
    * The #IdeContext is the root #IdeObject used in the tree of
    * objects representing the project and the workings of the IDE.
-   *
-   * Since: 3.32
    */
   properties [PROP_CONTEXT] =
     g_param_spec_object ("context",
@@ -480,8 +474,6 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
    * system that is currently loaded for the project.
    *
    * The #IdeVcs is registered by an #IdeWorkbenchAddin when loading a project.
-   *
-   * Since: 3.32
    */
   properties [PROP_VCS] =
     g_param_spec_object ("vcs",
@@ -549,8 +541,6 @@ ide_workbench_find_addin (IdeWorkbench *self,
  * be created based on the kind of workspace you want to display to the user.
  *
  * Returns: an #IdeWorkbench
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_workbench_new (void)
@@ -566,8 +556,6 @@ ide_workbench_new (void)
  * Creates a new #IdeWorkbench using @context for the #IdeWorkbench:context.
  *
  * Returns: (transfer full): an #IdeWorkbench
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_workbench_new_for_context (IdeContext *context)
@@ -586,8 +574,6 @@ ide_workbench_new_for_context (IdeContext *context)
  * Gets the #IdeContext for the workbench.
  *
  * Returns: (transfer none): an #IdeContext
- *
- * Since: 3.32
  */
 IdeContext *
 ide_workbench_get_context (IdeWorkbench *self)
@@ -605,8 +591,6 @@ ide_workbench_get_context (IdeWorkbench *self)
  * Finds the #IdeWorkbench associated with a widget.
  *
  * Returns: (nullable) (transfer none): an #IdeWorkbench or %NULL
- *
- * Since: 3.32
  */
 IdeWorkbench *
 ide_workbench_from_widget (GtkWidget *widget)
@@ -622,7 +606,7 @@ ide_workbench_from_widget (GtkWidget *widget)
    * just need to get the toplevel window group property, and cast.
    */
 
-  if ((toplevel = gtk_widget_get_toplevel (widget)) &&
+  if ((toplevel = GTK_WIDGET (gtk_widget_get_native (widget))) &&
       GTK_IS_WINDOW (toplevel) &&
       (group = gtk_window_get_group (GTK_WINDOW (toplevel))) &&
       IDE_IS_WORKBENCH (group))
@@ -634,18 +618,16 @@ ide_workbench_from_widget (GtkWidget *widget)
 /**
  * ide_workbench_foreach_workspace:
  * @self: an #IdeWorkbench
- * @callback: (scope call): a #GtkCallback to call for each #IdeWorkspace
+ * @callback: (scope call): a #IdeWorkspaceCallback to call for each #IdeWorkspace
  * @user_data: user data for @callback
  *
  * Iterates the available workspaces in the workbench. Workspaces are iterated
  * in most-recently-used order.
- *
- * Since: 3.32
  */
 void
-ide_workbench_foreach_workspace (IdeWorkbench *self,
-                                 GtkCallback   callback,
-                                 gpointer      user_data)
+ide_workbench_foreach_workspace (IdeWorkbench         *self,
+                                 IdeWorkspaceCallback  callback,
+                                 gpointer              user_data)
 {
   GList *copy;
 
@@ -660,7 +642,7 @@ ide_workbench_foreach_workspace (IdeWorkbench *self,
     {
       IdeWorkspace *workspace = iter->data;
       g_assert (IDE_IS_WORKSPACE (workspace));
-      callback (GTK_WIDGET (workspace), user_data);
+      callback (workspace, user_data);
     }
 
   g_list_free (copy);
@@ -674,13 +656,11 @@ ide_workbench_foreach_workspace (IdeWorkbench *self,
  *
  * Calls @callback for every page loaded in the workbench, by iterating
  * workspaces in order of most-recently-used.
- *
- * Since: 3.32
  */
 void
-ide_workbench_foreach_page (IdeWorkbench *self,
-                            GtkCallback   callback,
-                            gpointer      user_data)
+ide_workbench_foreach_page (IdeWorkbench    *self,
+                            IdePageCallback  callback,
+                            gpointer         user_data)
 {
   GList *copy;
 
@@ -699,15 +679,15 @@ ide_workbench_foreach_page (IdeWorkbench *self,
 }
 
 static void
-ide_workbench_workspace_has_toplevel_focus_cb (IdeWorkbench *self,
-                                               GParamSpec   *pspec,
-                                               IdeWorkspace *workspace)
+ide_workbench_workspace_is_active_cb (IdeWorkbench *self,
+                                      GParamSpec   *pspec,
+                                      IdeWorkspace *workspace)
 {
   g_assert (IDE_IS_WORKBENCH (self));
   g_assert (IDE_IS_WORKSPACE (workspace));
   g_assert (gtk_window_get_group (GTK_WINDOW (workspace)) == GTK_WINDOW_GROUP (self));
 
-  if (gtk_window_has_toplevel_focus (GTK_WINDOW (workspace)))
+  if (gtk_window_is_active (GTK_WINDOW (workspace)))
     {
       GList *mru_link = _ide_workspace_get_mru_link (workspace);
 
@@ -758,8 +738,6 @@ insert_action_groups_foreach_cb (IdeWorkspace *workspace,
  * @workspace: an #IdeWorkspace
  *
  * Adds @workspace to @workbench.
- *
- * Since: 3.32
  */
 void
 ide_workbench_add_workspace (IdeWorkbench *self,
@@ -811,8 +789,8 @@ ide_workbench_add_workspace (IdeWorkbench *self,
 
   /* Track toplevel focus changes to maintain a most-recently-used queue. */
   g_signal_connect_object (workspace,
-                           "notify::has-toplevel-focus",
-                           G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+                           "notify::is-active",
+                           G_CALLBACK (ide_workbench_workspace_is_active_cb),
                            self,
                            G_CONNECT_SWAPPED);
 
@@ -843,7 +821,7 @@ ide_workbench_add_workspace (IdeWorkbench *self,
 
   /* Load shortcuts for commands */
   command_manager = ide_command_manager_from_context (self->context);
-  _ide_command_manager_init_shortcuts (command_manager, workspace);
+  ide_command_manager_load_shortcuts (command_manager, GTK_NATIVE (workspace));
 }
 
 /**
@@ -852,8 +830,6 @@ ide_workbench_add_workspace (IdeWorkbench *self,
  * @workspace: an #IdeWorkspace
  *
  * Removes @workspace from @workbench.
- *
- * Since: 3.32
  */
 void
 ide_workbench_remove_workspace (IdeWorkbench *self,
@@ -873,12 +849,12 @@ ide_workbench_remove_workspace (IdeWorkbench *self,
   mru_link = _ide_workspace_get_mru_link (workspace);
   g_queue_unlink (&self->mru_queue, mru_link);
   g_signal_handlers_disconnect_by_func (workspace,
-                                        G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+                                        G_CALLBACK (ide_workbench_workspace_is_active_cb),
                                         self);
 
   /* Remove any shortcuts that were registered by command providers */
   command_manager = ide_command_manager_from_context (self->context);
-  _ide_command_manager_unload_shortcuts (command_manager, workspace);
+  ide_command_manager_unload_shortcuts (command_manager, GTK_NATIVE (workspace));
 
   /* Notify all the addins about losing the workspace. */
   if ((addins = ide_workbench_collect_addins (self)))
@@ -928,8 +904,6 @@ ide_workbench_remove_workspace (IdeWorkbench *self,
  *
  * Requests that @workspace be raised in the windows of @self, and
  * displayed to the user.
- *
- * Since: 3.32
  */
 void
 ide_workbench_focus_workspace (IdeWorkbench *self,
@@ -1005,7 +979,7 @@ ide_workbench_load_project_completed (IdeWorkbench *self,
 
   /* Give workspaces access to the various GActionGroups */
   ide_workbench_foreach_workspace (self,
-                                   (GtkCallback)insert_action_groups_foreach_cb,
+                                   (IdeWorkspaceCallback)insert_action_groups_foreach_cb,
                                    self);
 
   /* Notify addins that projects have loaded */
@@ -1128,8 +1102,6 @@ ide_workbench_init_foundry_cb (GObject      *object,
  *
  * @callback should call ide_workbench_load_project_finish() to obtain the
  * result of the open request.
- *
- * Since: 3.32
  */
 void
 ide_workbench_load_project_async (IdeWorkbench        *self,
@@ -1284,8 +1256,6 @@ ide_workbench_load_project_async (IdeWorkbench        *self,
  *
  * Returns: %TRUE if the project was successfully opened; otherwise %FALSE
  *   and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_workbench_load_project_finish (IdeWorkbench  *self,
@@ -1391,13 +1361,38 @@ ide_workbench_action_reload_all (IdeWorkbench *self,
   ide_buffer_manager_reload_all_async (bufmgr, NULL, NULL, NULL);
 }
 
+static void
+ide_workbench_action_open_response_cb (IdeWorkbench         *self,
+                                       int                   response,
+                                       GtkFileChooserNative *chooser)
+{
+  g_assert (IDE_IS_WORKBENCH (self));
+  g_assert (GTK_IS_FILE_CHOOSER_NATIVE (chooser));
+
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      g_autoptr(GListModel) model = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (chooser));
+      guint n_items = g_list_model_get_n_items (model);
+
+      for (guint i = 0; i < n_items; i++)
+        {
+          g_autoptr(GFile) file = g_list_model_get_item (model, i);
+
+          g_assert (G_IS_FILE (file));
+
+          ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL);
+        }
+    }
+
+  gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
+}
+
 static void
 ide_workbench_action_open (IdeWorkbench *self,
                            GVariant     *param)
 {
   GtkFileChooserNative *chooser;
   IdeWorkspace *workspace;
-  gint ret;
 
   g_assert (IDE_IS_WORKBENCH (self));
   g_assert (param == NULL);
@@ -1410,26 +1405,15 @@ ide_workbench_action_open (IdeWorkbench *self,
                                          _("_Open"),
                                          _("_Cancel"));
   gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (chooser), FALSE);
-  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), FALSE);
   gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
 
-  ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (chooser));
-
-  if (ret == GTK_RESPONSE_ACCEPT)
-    {
-      g_autoslist(GFile) files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (chooser));
-
-      for (const GSList *iter = files; iter; iter = iter->next)
-        {
-          GFile *file = iter->data;
-
-          g_assert (G_IS_FILE (file));
-
-          ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL);
-        }
-    }
+  g_signal_connect_object (chooser,
+                           "response",
+                           G_CALLBACK (ide_workbench_action_open_response_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
 
-  gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
+  gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser));
 }
 
 /**
@@ -1439,8 +1423,6 @@ ide_workbench_action_open (IdeWorkbench *self,
  * Gets the search engine for the workbench, if any.
  *
  * Returns: (transfer none): an #IdeSearchEngine
- *
- * Since: 3.32
  */
 IdeSearchEngine *
 ide_workbench_get_search_engine (IdeWorkbench *self)
@@ -1463,8 +1445,6 @@ ide_workbench_get_search_engine (IdeWorkbench *self)
  * currently, loading.
  *
  * Returns: (transfer none) (nullable): an #IdeProjectInfo or %NULL
- *
- * Since: 3.32
  */
 IdeProjectInfo *
 ide_workbench_get_project_info (IdeWorkbench *self)
@@ -1508,7 +1488,15 @@ ide_workbench_unload_project_completed (IdeWorkbench *self,
   g_assert (IDE_IS_TASK (task));
 
   g_clear_object (&self->addins);
-  ide_workbench_foreach_workspace (self, (GtkCallback)gtk_widget_destroy, NULL);
+
+  while (self->mru_queue.head != NULL)
+    {
+      IdeWorkspace *workspace = self->mru_queue.head->data;
+
+      g_assert (IDE_IS_WORKSPACE (workspace));
+
+      gtk_window_destroy (GTK_WINDOW (workspace));
+    }
 
   _ide_foundry_unload_async (self->context,
                              ide_task_get_cancellable (task),
@@ -1561,8 +1549,6 @@ ide_workbench_unload_project_cb (GObject      *object,
  *
  * All #IdeWorkspace windows will be closed after calling this
  * function.
- *
- * Since: 3.32
  */
 void
 ide_workbench_unload_async (IdeWorkbench        *self,
@@ -1651,8 +1637,6 @@ ide_workbench_unload_async (IdeWorkbench        *self,
  *
  * Returns: %TRUE if the workbench was unloaded successfully,
  *   otherwise %FALSE and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_workbench_unload_finish (IdeWorkbench *self,
@@ -1709,8 +1693,6 @@ ide_workbench_open_all_cb (GObject      *object,
  *
  * Call ide_workbench_open_finish() from @callback to complete this
  * operation.
- *
- * Since: 3.32
  */
 void
 ide_workbench_open_all_async (IdeWorkbench         *self,
@@ -1776,8 +1758,6 @@ ide_workbench_open_all_async (IdeWorkbench         *self,
  * module name of the plugin.
  *
  * @flags may be ignored by some backends.
- *
- * Since: 3.32
  */
 void
 ide_workbench_open_async (IdeWorkbench        *self,
@@ -2005,8 +1985,6 @@ ide_workbench_open_query_info_cb (GObject      *object,
  *
  * Use ide_workbench_open_finish() to receive teh result of this
  * asynchronous operation.
- *
- * Since: 3.32
  */
 void
 ide_workbench_open_at_async (IdeWorkbench        *self,
@@ -2101,8 +2079,6 @@ ide_workbench_open_at_async (IdeWorkbench        *self,
  *
  * Returns: %TRUE if the file was successfully opened; otherwise
  *   %FALSE and @error is set.
- *
- * Since: 3.32
  */
 gboolean
 ide_workbench_open_finish (IdeWorkbench  *self,
@@ -2123,8 +2099,6 @@ ide_workbench_open_finish (IdeWorkbench  *self,
  * deliver events such as opening new pages.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
- *
- * Since: 3.32
  */
 IdeWorkspace *
 ide_workbench_get_current_workspace (IdeWorkbench *self)
@@ -2142,8 +2116,6 @@ ide_workbench_get_current_workspace (IdeWorkbench *self)
  * @self: a #IdeWorkbench
  *
  * This function will attempt to raise the most recently focused workspace.
- *
- * Since: 3.32
  */
 void
 ide_workbench_activate (IdeWorkbench *self)
@@ -2180,8 +2152,6 @@ ide_workbench_propagate_vcs_cb (PeasExtensionSet *set,
  * Gets the #IdeVcs that has been loaded for the workbench, if any.
  *
  * Returns: (transfer none) (nullable): an #IdeVcs or %NULL
- *
- * Since: 3.32
  */
 IdeVcs *
 ide_workbench_get_vcs (IdeWorkbench *self)
@@ -2198,8 +2168,6 @@ ide_workbench_get_vcs (IdeWorkbench *self)
  * Gets the #IdeVcsMonitor for the workbench, if any.
  *
  * Returns: (transfer none) (nullable): an #IdeVcsMonitor or %NULL
- *
- * Since: 3.32
  */
 IdeVcsMonitor *
 ide_workbench_get_vcs_monitor (IdeWorkbench *self)
@@ -2248,8 +2216,6 @@ ide_workbench_vcs_notify_branch_name_cb (IdeWorkbench *self,
  * @vcs: (nullable): an #IdeVcs
  *
  * Sets the #IdeVcs for the workbench.
- *
- * Since: 3.32
  */
 void
 ide_workbench_set_vcs (IdeWorkbench *self,
@@ -2303,8 +2269,6 @@ ide_workbench_set_vcs (IdeWorkbench *self,
  * Gets the #IdeBuildSystem for the workbench, if any.
  *
  * Returns: (transfer none) (nullable): an #IdeBuildSystem or %NULL
- *
- * Since: 3.32
  */
 IdeBuildSystem *
 ide_workbench_get_build_system (IdeWorkbench *self)
@@ -2337,8 +2301,6 @@ remove_non_matching_build_systems_cb (IdeObject      *child,
  * If @build_system is %NULL, then a fallback build system will be used
  * instead. It does not provide building capabilities, but allows for some
  * components that require a build system to continue functioning.
- *
- * Since: 3.32
  */
 void
 ide_workbench_set_build_system (IdeWorkbench   *self,
@@ -2387,8 +2349,6 @@ ide_workbench_set_build_system (IdeWorkbench   *self,
  * Gets the most-recently-used workspace that matches @type.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
- *
- * Since: 3.32
  */
 IdeWorkspace *
 ide_workbench_get_workspace_by_type (IdeWorkbench *self,
@@ -2426,8 +2386,6 @@ _ide_workbench_is_last_workspace (IdeWorkbench *self,
  * workbench.
  *
  * Returns: %TRUE if the workbench has a project
- *
- * Since: 3.32
  */
 gboolean
 ide_workbench_has_project (IdeWorkbench *self)
@@ -2446,8 +2404,6 @@ ide_workbench_has_project (IdeWorkbench *self)
  * Finds the addin (if any) matching the plugin's @module_name.
  *
  * Returns: (transfer none) (nullable): an #IdeWorkbenchAddin or %NULL
- *
- * Since: 3.32
  */
 IdeWorkbenchAddin *
 ide_workbench_addin_find_by_module_name (IdeWorkbench *workbench,
@@ -2529,8 +2485,6 @@ ide_workbench_resolve_file_worker (IdeTask      *task,
  *
  * If no file was discovered, some attempt will be made to locate a file
  * that matches appropriately.
- *
- * Since: 3.32
  */
 void
 ide_workbench_resolve_file_async (IdeWorkbench        *self,
@@ -2585,8 +2539,6 @@ ide_workbench_resolve_file_async (IdeWorkbench        *self,
  * Completes an asynchronous request to ide_workbench_resolve_file_async().
  *
  * Returns: (transfer full): a #GFile, or %NULL and @error is set
- *
- * Since: 3.32
  */
 GFile *
 ide_workbench_resolve_file_finish (IdeWorkbench  *self,
diff --git a/src/libide/gui/ide-workspace-private.h b/src/libide/gui/ide-workspace-private.h
new file mode 100644
index 000000000..60f859656
--- /dev/null
+++ b/src/libide/gui/ide-workspace-private.h
@@ -0,0 +1,38 @@
+/* ide-workspace-private.h
+ *
+ * Copyright 2022 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+void   _ide_workspace_init_actions           (IdeWorkspace        *self);
+GList *_ide_workspace_get_mru_link           (IdeWorkspace        *self);
+void   _ide_workspace_add_page_mru           (IdeWorkspace        *self,
+                                              GList               *mru_link);
+void   _ide_workspace_remove_page_mru        (IdeWorkspace        *self,
+                                              GList               *mru_link);
+void   _ide_workspace_move_front_page_mru    (IdeWorkspace        *workspace,
+                                              GList               *mru_link);
+void   _ide_workspace_set_context            (IdeWorkspace        *workspace,
+                                              IdeContext          *context);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workspace.h b/src/libide/gui/ide-workspace.h
index 5b0fcf1a9..22885c462 100644
--- a/src/libide/gui/ide-workspace.h
+++ b/src/libide/gui/ide-workspace.h
@@ -31,7 +31,6 @@
 
 #include "ide-header-bar.h"
 #include "ide-page.h"
-#include "ide-surface.h"
 
 G_BEGIN_DECLS
 
@@ -49,13 +48,12 @@ struct _IdeWorkspaceClass
 
   const gchar *kind;
 
-  void (*context_set)  (IdeWorkspace    *self,
-                        IdeContext      *context);
-  void (*foreach_page) (IdeWorkspace    *self,
-                        IdePageCallback  callback,
-                        gpointer         user_data);
-  void (*surface_set)  (IdeWorkspace    *self,
-                        IdeSurface      *surface);
+  void     (*context_set)          (IdeWorkspace    *self,
+                                    IdeContext      *context);
+  void     (*foreach_page)         (IdeWorkspace    *self,
+                                    IdePageCallback  callback,
+                                    gpointer         user_data);
+  IdePage *(*get_most_recent_page) (IdeWorkspace    *self);
 
   /*< private >*/
   gpointer _reserved[8];
@@ -75,26 +73,6 @@ void          ide_workspace_foreach_page             (IdeWorkspace      *self,
                                                       IdePageCallback    callback,
                                                       gpointer           user_data);
 IDE_AVAILABLE_IN_ALL
-void          ide_workspace_foreach_surface          (IdeWorkspace       *self,
-                                                      IdeSurfaceCallback  callback,
-                                                      gpointer            user_data);
-IDE_AVAILABLE_IN_ALL
-void          ide_workspace_add_surface              (IdeWorkspace      *self,
-                                                      IdeSurface        *surface);
-IDE_AVAILABLE_IN_ALL
-IdeSurface   *ide_workspace_get_surface_by_name      (IdeWorkspace      *self,
-                                                      const gchar       *name);
-IDE_AVAILABLE_IN_ALL
-void          ide_workspace_set_visible_surface_name (IdeWorkspace      *self,
-                                                      const gchar       *visible_surface_name);
-IDE_AVAILABLE_IN_ALL
-IdeSurface   *ide_workspace_get_visible_surface      (IdeWorkspace      *self);
-IDE_AVAILABLE_IN_ALL
-void          ide_workspace_set_visible_surface      (IdeWorkspace      *self,
-                                                      IdeSurface        *surface);
-IDE_AVAILABLE_IN_ALL
-GtkOverlay   *ide_workspace_get_overlay              (IdeWorkspace      *self);
-IDE_AVAILABLE_IN_ALL
 IdePage      *ide_workspace_get_most_recent_page     (IdeWorkspace      *self);
 
 G_END_DECLS
diff --git a/src/libide/gui/meson.build b/src/libide/gui/meson.build
index 7c7a8cbc7..dba49abaa 100644
--- a/src/libide/gui/meson.build
+++ b/src/libide/gui/meson.build
@@ -140,6 +140,7 @@ libide_gui_deps = [
 
   libide_core_dep,
   libide_io_dep,
+  libide_commands_dep,
   libide_foundry_dep,
   libide_gtk_dep,
   libide_debugger_dep,


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