[gnome-builder] libide-greeter: port to GTK 4



commit 3d578761007e4473dee895e6b0c3389c71217ea9
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 21:57:41 2022 -0700

    libide-greeter: port to GTK 4
    
     - Port to GTK 4 and libadwaita
     - Use libide-gui abstractions for workspace
     - Remove truncate model copy
     - Remove clone surface (it belongs in vcsui plugin)
     - Remove libdazzle usage

 src/libide/greeter/gtk/menus.ui                    |  91 ++++
 src/libide/greeter/ide-clone-surface.c             | 605 ---------------------
 src/libide/greeter/ide-clone-surface.h             |  42 --
 src/libide/greeter/ide-clone-surface.ui            | 388 -------------
 src/libide/greeter/ide-greeter-buttons-section.c   |  61 ++-
 src/libide/greeter/ide-greeter-buttons-section.h   |   2 +-
 src/libide/greeter/ide-greeter-private.h           |   3 +-
 src/libide/greeter/ide-greeter-row.c               |  63 +--
 src/libide/greeter/ide-greeter-row.h               |  18 +-
 src/libide/greeter/ide-greeter-row.ui              |  39 +-
 src/libide/greeter/ide-greeter-section.c           |  12 +-
 src/libide/greeter/ide-greeter-section.h           |  26 +-
 src/libide/greeter/ide-greeter-workspace-actions.c |   9 +-
 .../greeter/ide-greeter-workspace-shortcuts.c      |  50 --
 src/libide/greeter/ide-greeter-workspace.c         | 547 ++++++++++++-------
 src/libide/greeter/ide-greeter-workspace.h         |  45 +-
 src/libide/greeter/ide-greeter-workspace.ui        | 122 ++---
 src/libide/greeter/ide-truncate-model.c            | 358 ------------
 src/libide/greeter/ide-truncate-model.h            |  41 --
 src/libide/greeter/libide-greeter.gresource.xml    |   5 +-
 src/libide/greeter/libide-greeter.h                |   1 -
 src/libide/greeter/meson.build                     |   9 +-
 22 files changed, 643 insertions(+), 1894 deletions(-)
---
diff --git a/src/libide/greeter/gtk/menus.ui b/src/libide/greeter/gtk/menus.ui
new file mode 100644
index 000000000..3d0100fd7
--- /dev/null
+++ b/src/libide/greeter/gtk/menus.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="ide-greeter-workspace-menu">
+    <section id="ide-greeter-workspace-menu-theme">
+      <item>
+        <attribute name="custom">theme_selector</attribute>
+      </item>
+    </section>
+    <section id="ide-greeter-workspace-menu-projects">
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-clone</attribute>
+        <attribute name="label" translatable="yes">_Clone Repository…</attribute>
+        <attribute name="action">greeter.page</attribute>
+        <attribute name="target" type="s">'clone'</attribute>
+      </item>
+    </section>
+    <section id="ide-greeter-workspace-menu-close">
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-close</attribute>
+        <attribute name="label" translatable="yes">Close</attribute>
+        <attribute name="action">win.close</attribute>
+      </item>
+    </section>
+    <section id="ide-greeter-workspace-menu-app">
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-preferences</attribute>
+        <attribute name="label" translatable="yes">Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+        <attribute name="accel">&lt;primary&gt;comma</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-shortcuts</attribute>
+        <attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
+        <attribute name="action">win.show-help-overlay</attribute>
+        <attribute name="accel">&lt;primary&gt;question</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-help</attribute>
+        <attribute name="label" translatable="yes">Help</attribute>
+        <attribute name="action">app.help</attribute>
+        <attribute name="accel">F1</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-about</attribute>
+        <attribute name="label" translatable="yes">About Builder</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+    <section id="ide-greeter-workspace-menu-quit-section">
+      <item>
+        <attribute name="id">ide-greeter-workspace-menu-quit</attribute>
+        <attribute name="label" translatable="yes">Quit</attribute>
+        <attribute name="action">app.quit</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-primary-workspace-menu">
+    <section id="ide-primary-workspace-menu-projects-section">
+      <item>
+        <attribute name="id">ide-primary-workspace-menu-open</attribute>
+        <attribute name="label" translatable="yes">Open a _Project…</attribute>
+        <attribute name="action">app.present-greeter-with-page</attribute>
+        <attribute name="target" type="s">''</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-primary-workspace-menu-clone</attribute>
+        <attribute name="label" translatable="yes">_Clone Repository…</attribute>
+        <attribute name="action">app.present-greeter-with-page</attribute>
+        <attribute name="target" type="s">'clone'</attribute>
+      </item>
+    </section>
+  </menu>
+  <menu id="ide-editor-workspace-menu">
+    <section id="ide-editor-workspace-menu-theme-section">
+    </section>
+    <section id="ide-editor-workspace-menu-projects-section">
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-open</attribute>
+        <attribute name="label" translatable="yes">Open a _Project…</attribute>
+        <attribute name="action">app.present-greeter-with-page</attribute>
+        <attribute name="target" type="s">''</attribute>
+      </item>
+      <item>
+        <attribute name="id">ide-editor-workspace-menu-clone</attribute>
+        <attribute name="label" translatable="yes">_Clone Repository…</attribute>
+        <attribute name="action">app.present-greeter-with-page</attribute>
+        <attribute name="target" type="s">'clone'</attribute>
+      </item>
+    </section>
+  </menu>
+</interface>
diff --git a/src/libide/greeter/ide-greeter-buttons-section.c 
b/src/libide/greeter/ide-greeter-buttons-section.c
index fba74f658..0f36f3bdb 100644
--- a/src/libide/greeter/ide-greeter-buttons-section.c
+++ b/src/libide/greeter/ide-greeter-buttons-section.c
@@ -28,24 +28,40 @@
 
 struct _IdeGreeterButtonsSection
 {
-  GtkBin  parent_instance;
-  GtkBox *box;
+  GtkWidget  parent_instance;
+  GtkBox    *box;
 };
 
-G_DEFINE_FINAL_TYPE_WITH_CODE (IdeGreeterButtonsSection, ide_greeter_buttons_section, GTK_TYPE_BIN,
-                         G_IMPLEMENT_INTERFACE (IDE_TYPE_GREETER_SECTION, NULL))
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeGreeterButtonsSection, ide_greeter_buttons_section, GTK_TYPE_WIDGET,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_GREETER_SECTION, NULL))
+
+static void
+ide_greeter_buttons_section_dispose (GObject *object)
+{
+  IdeGreeterButtonsSection *self = (IdeGreeterButtonsSection *)object;
+
+  g_clear_pointer ((GtkWidget **)&self->box, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (ide_greeter_buttons_section_parent_class)->dispose (object);
+}
 
 static void
 ide_greeter_buttons_section_class_init (IdeGreeterButtonsSectionClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = ide_greeter_buttons_section_dispose;
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
 static void
 ide_greeter_buttons_section_init (IdeGreeterButtonsSection *self)
 {
-  self->box = g_object_new (DZL_TYPE_PRIORITY_BOX,
-                            "margin-bottom", 6,
-                            "margin-top", 6,
+  self->box = g_object_new (GTK_TYPE_BOX,
+                            "margin-bottom", 3,
+                            "margin-top", 3,
                             "orientation", GTK_ORIENTATION_HORIZONTAL,
                             "homogeneous", TRUE,
                             "halign", GTK_ALIGN_CENTER,
@@ -54,7 +70,7 @@ ide_greeter_buttons_section_init (IdeGreeterButtonsSection *self)
                             "width-request", 600,
                             "visible", TRUE,
                             NULL);
-  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->box));
+  gtk_widget_set_parent (GTK_WIDGET (self->box), GTK_WIDGET (self));
 
   ide_greeter_buttons_section_add_button (self,
                                           0,
@@ -64,26 +80,31 @@ ide_greeter_buttons_section_init (IdeGreeterButtonsSection *self)
                                                         "action-name", "win.open",
                                                         "use-underline", TRUE,
                                                         NULL));
-  ide_greeter_buttons_section_add_button (self,
-                                          100,
-                                          g_object_new (GTK_TYPE_BUTTON,
-                                                        "label", _("_Clone Repository…"),
-                                                        "visible", TRUE,
-                                                        "action-name", "win.surface",
-                                                        "action-target", g_variant_new_string ("clone"),
-                                                        "use-underline", TRUE,
-                                                        NULL));
 }
 
+#define GET_PRIORITY(w)   GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"PRIORITY"))
+#define SET_PRIORITY(w,i) g_object_set_data(G_OBJECT(w),"PRIORITY",GINT_TO_POINTER(i))
+
 void
 ide_greeter_buttons_section_add_button (IdeGreeterButtonsSection *self,
                                         gint                      priority,
                                         GtkWidget                *widget)
 {
+  GtkWidget *sibling = NULL;
+
   g_return_if_fail (IDE_IS_GREETER_BUTTONS_SECTION (self));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  gtk_container_add_with_properties (GTK_CONTAINER (self->box), widget,
-                                     "priority", priority,
-                                     NULL);
+  SET_PRIORITY (widget, priority);
+
+  for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->box));
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (priority < GET_PRIORITY (child))
+        break;
+      sibling = child;
+    }
+
+  gtk_box_insert_child_after (self->box, widget, sibling);
 }
diff --git a/src/libide/greeter/ide-greeter-buttons-section.h 
b/src/libide/greeter/ide-greeter-buttons-section.h
index f25372ddd..7ad158ca2 100644
--- a/src/libide/greeter/ide-greeter-buttons-section.h
+++ b/src/libide/greeter/ide-greeter-buttons-section.h
@@ -26,7 +26,7 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_GREETER_BUTTONS_SECTION (ide_greeter_buttons_section_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGreeterButtonsSection, ide_greeter_buttons_section, IDE, GREETER_BUTTONS_SECTION, 
GtkBin)
+G_DECLARE_FINAL_TYPE (IdeGreeterButtonsSection, ide_greeter_buttons_section, IDE, GREETER_BUTTONS_SECTION, 
GtkWidget)
 
 IdeGreeterButtonsSection *ide_greeter_buttons_section_new        (void);
 void                      ide_greeter_buttons_section_add_button (IdeGreeterButtonsSection *self,
diff --git a/src/libide/greeter/ide-greeter-private.h b/src/libide/greeter/ide-greeter-private.h
index 1b41ee4d6..ea04ed2e4 100644
--- a/src/libide/greeter/ide-greeter-private.h
+++ b/src/libide/greeter/ide-greeter-private.h
@@ -26,7 +26,6 @@
 
 G_BEGIN_DECLS
 
-void _ide_greeter_workspace_init_actions   (IdeGreeterWorkspace *self);
-void _ide_greeter_workspace_init_shortcuts (IdeGreeterWorkspace *self);
+void _ide_greeter_workspace_init_actions (IdeGreeterWorkspace *self);
 
 G_END_DECLS
diff --git a/src/libide/greeter/ide-greeter-row.c b/src/libide/greeter/ide-greeter-row.c
index 069f5bbe2..9e5673be2 100644
--- a/src/libide/greeter/ide-greeter-row.c
+++ b/src/libide/greeter/ide-greeter-row.c
@@ -23,9 +23,11 @@
 #include "config.h"
 
 #include <glib/gi18n.h>
-#include <libide-projects.h>
 #include <stdlib.h>
 
+#include <libide-gtk.h>
+#include <libide-projects.h>
+
 #include "ide-project-info-private.h"
 
 #include "ide-greeter-row.h"
@@ -36,6 +38,8 @@ typedef struct
 
   /* Template Widgets */
   GtkCheckButton *check_button;
+  GtkRevealer    *revealer;
+  GtkImage       *next_image;
   GtkLabel       *title;
   GtkLabel       *subtitle;
   GtkImage       *image;
@@ -74,23 +78,14 @@ ide_greeter_row_new (void)
 }
 
 static void
-ide_greeter_row_get_preferred_width (GtkWidget *widget,
-                                     gint      *min_width,
-                                     gint      *nat_width)
-{
-  *min_width = 600;
-  *nat_width = 600;
-}
-
-static void
-ide_greeter_row_finalize (GObject *object)
+ide_greeter_row_dispose (GObject *object)
 {
   IdeGreeterRow *self = (IdeGreeterRow *)object;
   IdeGreeterRowPrivate *priv = ide_greeter_row_get_instance_private (self);
 
   g_clear_object (&priv->project_info);
 
-  G_OBJECT_CLASS (ide_greeter_row_parent_class)->finalize (object);
+  G_OBJECT_CLASS (ide_greeter_row_parent_class)->dispose (object);
 }
 
 static void
@@ -110,7 +105,7 @@ ide_greeter_row_get_property (GObject    *object,
 
     case PROP_SELECTED:
       g_value_set_boolean (value,
-                           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->check_button)));
+                           gtk_check_button_get_active (GTK_CHECK_BUTTON (priv->check_button)));
       break;
 
     default:
@@ -134,8 +129,8 @@ ide_greeter_row_set_property (GObject      *object,
       break;
 
     case PROP_SELECTED:
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check_button),
-                                    g_value_get_boolean (value));
+      gtk_check_button_set_active (GTK_CHECK_BUTTON (priv->check_button),
+                                   g_value_get_boolean (value));
       break;
 
     default:
@@ -149,19 +144,15 @@ ide_greeter_row_class_init (IdeGreeterRowClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  object_class->finalize = ide_greeter_row_finalize;
+  object_class->dispose = ide_greeter_row_dispose;
   object_class->get_property = ide_greeter_row_get_property;
   object_class->set_property = ide_greeter_row_set_property;
 
-  widget_class->get_preferred_width = ide_greeter_row_get_preferred_width;
-
   /**
    * IdeGreeterRow:project-info:
    *
    * The "project-info" property contains information about the project
    * to be displayed.
-   *
-   * Since: 3.32
    */
   properties [PROP_PROJECT_INFO] =
     g_param_spec_object ("project-info",
@@ -179,9 +170,11 @@ ide_greeter_row_class_init (IdeGreeterRowClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
-  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-greeter-row.ui");
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-greeter/ide-greeter-row.ui");
   gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, check_button);
   gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, image);
+  gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, next_image);
+  gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, revealer);
   gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, subtitle);
   gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, tags);
   gtk_widget_class_bind_template_child_private (widget_class, IdeGreeterRow, title);
@@ -211,15 +204,16 @@ static void
 ide_greeter_row_clear (IdeGreeterRow *self)
 {
   IdeGreeterRowPrivate *priv = ide_greeter_row_get_instance_private (self);
+  GtkWidget *child;
 
   g_assert (IDE_IS_GREETER_ROW (self));
 
   g_object_set (priv->image, "icon-name", NULL, NULL);
   gtk_label_set_label (priv->title, NULL);
   gtk_label_set_label (priv->subtitle, NULL);
-  gtk_container_foreach (GTK_CONTAINER (priv->tags),
-                         (GtkCallback)gtk_widget_destroy,
-                         NULL);
+
+  while ((child = gtk_widget_get_first_child (GTK_WIDGET (priv->tags))))
+    gtk_box_remove (priv->tags, child);
 }
 
 /**
@@ -229,8 +223,6 @@ ide_greeter_row_clear (IdeGreeterRow *self)
  * Gets the #IdeGreeterRow:project-info property.
  *
  * Returns: (transfer none) (nullable): an #IdeProjectInfo or %NULL
- *
- * Since: 3.32
  */
 IdeProjectInfo *
 ide_greeter_row_get_project_info (IdeGreeterRow *self)
@@ -311,13 +303,14 @@ ide_greeter_row_set_project_info (IdeGreeterRow  *self,
           for (guint i = 0; i < parts->len; i++)
             {
               const gchar *key = g_ptr_array_index (parts, i);
-              DzlPillBox *tag;
+              GtkLabel *tag;
 
-              tag = g_object_new (DZL_TYPE_PILL_BOX,
-                                  "visible", TRUE,
+              tag = g_object_new (GTK_TYPE_LABEL,
                                   "label", key,
+                                  "css-name", "button",
+                                  "css-classes", IDE_STRV_INIT ("pill", "small"),
                                   NULL);
-              gtk_container_add (GTK_CONTAINER (priv->tags), GTK_WIDGET (tag));
+              gtk_box_append (priv->tags, GTK_WIDGET (tag));
             }
 
           if (icon != NULL)
@@ -339,8 +332,6 @@ ide_greeter_row_set_project_info (IdeGreeterRow  *self,
  * Gets a new string containing the search text for the greeter row.
  *
  * Returns: (transfer full) (nullable): a string or %NULL
- *
- * Since: 3.32
  */
 gchar *
 ide_greeter_row_get_search_text (IdeGreeterRow *self)
@@ -375,5 +366,11 @@ ide_greeter_row_set_selection_mode (IdeGreeterRow *self,
 
   g_return_if_fail (IDE_IS_GREETER_ROW (self));
 
-  gtk_widget_set_visible (GTK_WIDGET (priv->check_button), selection_mode);
+  gtk_revealer_set_reveal_child (priv->revealer, selection_mode);
+  ide_object_animate (priv->next_image,
+                      IDE_ANIMATION_EASE_OUT_CUBIC,
+                      300,
+                      NULL,
+                      "opacity", selection_mode ? 0.0 : 1.0,
+                      NULL);
 }
diff --git a/src/libide/greeter/ide-greeter-row.h b/src/libide/greeter/ide-greeter-row.h
index 8857668c4..ebd7dd352 100644
--- a/src/libide/greeter/ide-greeter-row.h
+++ b/src/libide/greeter/ide-greeter-row.h
@@ -25,35 +25,33 @@
 #endif
 
 #include <gtk/gtk.h>
+
 #include <libide-projects.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_GREETER_ROW (ide_greeter_row_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_DERIVABLE_TYPE (IdeGreeterRow, ide_greeter_row, IDE, GREETER_ROW, GtkListBoxRow)
 
 struct _IdeGreeterRowClass
 {
   GtkListBoxRowClass parent_class;
-
-  /*< private >*/
-  gpointer _reserved[8];
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeGreeterRow  *ide_greeter_row_new                (void);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeProjectInfo *ide_greeter_row_get_project_info   (IdeGreeterRow  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void            ide_greeter_row_set_project_info   (IdeGreeterRow  *self,
                                                     IdeProjectInfo *project_info);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gchar          *ide_greeter_row_get_search_text    (IdeGreeterRow  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean        ide_greeter_row_get_selection_mode (IdeGreeterRow  *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void            ide_greeter_row_set_selection_mode (IdeGreeterRow  *self,
                                                     gboolean        selection_mode);
 
diff --git a/src/libide/greeter/ide-greeter-row.ui b/src/libide/greeter/ide-greeter-row.ui
index 8f6d301d9..46b623979 100644
--- a/src/libide/greeter/ide-greeter-row.ui
+++ b/src/libide/greeter/ide-greeter-row.ui
@@ -6,34 +6,39 @@
       <object class="GtkBox">
         <property name="margin-top">12</property>
         <property name="margin-bottom">12</property>
-        <property name="margin-start">18</property>
-        <property name="margin-end">18</property>
+        <property name="margin-start">12</property>
+        <property name="margin-end">12</property>
         <property name="orientation">horizontal</property>
-        <property name="spacing">12</property>
-        <property name="visible">true</property>
         <child>
-          <object class="GtkCheckButton" id="check_button">
-            <property name="margin-end">6</property>
-            <property name="valign">center</property>
+          <object class="GtkRevealer" id="revealer">
+            <property name="reveal-child">false</property>
+            <property name="transition-type">slide-right</property>
+            <child>
+              <object class="GtkCheckButton" id="check_button">
+                <property name="margin-end">6</property>
+                <property name="valign">center</property>
+                <style>
+                  <class name="selection-mode"/>
+                </style>
+              </object>
+            </child>
           </object>
         </child>
         <child>
           <object class="GtkImage" id="image">
-            <style>
-              <class name="lowres-icon"/>
-            </style>
+            <property name="visible">false</property>
+            <property name="margin-end">12</property>
           </object>
         </child>
         <child>
           <object class="GtkBox">
+            <property name="margin-start">6</property>
             <property name="hexpand">true</property>
             <property name="orientation">vertical</property>
             <property name="spacing">6</property>
-            <property name="visible">true</property>
             <child>
               <object class="GtkLabel" id="title">
                 <property name="ellipsize">end</property>
-                <property name="visible">true</property>
                 <property name="xalign">0.0</property>
                 <attributes>
                   <attribute name="weight" value="bold"/>
@@ -43,7 +48,6 @@
             <child>
               <object class="GtkLabel" id="subtitle">
                 <property name="ellipsize">end</property>
-                <property name="visible">true</property>
                 <property name="xalign">0.0</property>
                 <style>
                   <class name="dim-label"/>
@@ -54,16 +58,15 @@
         </child>
         <child>
           <object class="GtkBox" id="tags">
-            <property name="spacing">6</property>
+            <property name="margin-start">6</property>
+            <property name="spacing">3</property>
             <property name="valign">center</property>
-            <property name="visible">true</property>
           </object>
         </child>
         <child>
-          <object class="GtkImage">
+          <object class="GtkImage" id="next_image">
             <property name="icon-name">go-next-symbolic</property>
-            <property name="margin-start">6</property>
-            <property name="visible">true</property>
+            <property name="margin-start">12</property>
           </object>
         </child>
       </object>
diff --git a/src/libide/greeter/ide-greeter-section.c b/src/libide/greeter/ide-greeter-section.c
index cc15cfa95..9f9634f12 100644
--- a/src/libide/greeter/ide-greeter-section.c
+++ b/src/libide/greeter/ide-greeter-section.c
@@ -46,8 +46,6 @@ ide_greeter_section_default_init (IdeGreeterSectionInterface *iface)
    *
    * Use ide_greeter_section_emit_project_activated() to activate
    * this signal.
-   *
-   * Since: 3.32
    */
   signals [PROJECT_ACTIVATED] =
     g_signal_new ("project-activated",
@@ -66,8 +64,6 @@ ide_greeter_section_default_init (IdeGreeterSectionInterface *iface)
  * sorted first in the list of sections.
  *
  * Returns: the priority for the section
- *
- * Since: 3.32
  */
 gint
 ide_greeter_section_get_priority (IdeGreeterSection *self)
@@ -83,17 +79,15 @@ ide_greeter_section_get_priority (IdeGreeterSection *self)
 /**
  * ide_greeter_section_filter:
  * @self: a #IdeGreeterSection
- * @spec: (nullable): a #DzlPatternSpec or %NULL
+ * @spec: (nullable): a #IdePatternSpec or %NULL
  *
  * Refilter the visibile items based on the current search.
  *
  * Returns: %TRUE if at least one element matched.
- *
- * Since: 3.32
  */
 gboolean
 ide_greeter_section_filter (IdeGreeterSection *self,
-                            DzlPatternSpec    *spec)
+                            IdePatternSpec    *spec)
 {
   g_return_val_if_fail (IDE_IS_GREETER_SECTION (self), FALSE);
 
@@ -126,8 +120,6 @@ ide_greeter_section_emit_project_activated (IdeGreeterSection *self,
  * If no item matched, then return %FALSE.
  *
  * Returns: %TRUE if an item was activated
- *
- * Since: 3.32
  */
 gboolean
 ide_greeter_section_activate_first (IdeGreeterSection *self)
diff --git a/src/libide/greeter/ide-greeter-section.h b/src/libide/greeter/ide-greeter-section.h
index 8dc0f939a..4b3050084 100644
--- a/src/libide/greeter/ide-greeter-section.h
+++ b/src/libide/greeter/ide-greeter-section.h
@@ -20,15 +20,19 @@
 
 #pragma once
 
-#include <dazzle.h>
+#if !defined (IDE_GREETER_INSIDE) && !defined (IDE_GREETER_COMPILATION)
+# error "Only <libide-greeter.h> can be included directly."
+#endif
+
 #include <libide-core.h>
 #include <libide-projects.h>
+#include <libide-search.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_GREETER_SECTION (ide_greeter_section_get_type ())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_INTERFACE (IdeGreeterSection, ide_greeter_section, IDE, GREETER_SECTION, GtkWidget)
 
 struct _IdeGreeterSectionInterface
@@ -39,7 +43,7 @@ struct _IdeGreeterSectionInterface
                                   IdeProjectInfo    *project_info);
   gint     (*get_priority)       (IdeGreeterSection *self);
   gboolean (*filter)             (IdeGreeterSection *self,
-                                  DzlPatternSpec    *spec);
+                                  IdePatternSpec    *spec);
   gboolean (*activate_first)     (IdeGreeterSection *self);
   void     (*set_selection_mode) (IdeGreeterSection *self,
                                   gboolean           selection_mode);
@@ -47,22 +51,22 @@ struct _IdeGreeterSectionInterface
   void     (*purge_selected)     (IdeGreeterSection *self);
 };
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gint     ide_greeter_section_get_priority           (IdeGreeterSection *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean ide_greeter_section_filter                 (IdeGreeterSection *self,
-                                                     DzlPatternSpec    *spec);
-IDE_AVAILABLE_IN_3_32
+                                                     IdePatternSpec    *spec);
+IDE_AVAILABLE_IN_ALL
 void     ide_greeter_section_emit_project_activated (IdeGreeterSection *self,
                                                      IdeProjectInfo    *project_info);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean ide_greeter_section_activate_first         (IdeGreeterSection *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void     ide_greeter_section_set_selection_mode     (IdeGreeterSection *self,
                                                      gboolean           selection_mode);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void     ide_greeter_section_delete_selected        (IdeGreeterSection *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void     ide_greeter_section_purge_selected         (IdeGreeterSection *self);
 
 G_END_DECLS
diff --git a/src/libide/greeter/ide-greeter-workspace-actions.c 
b/src/libide/greeter/ide-greeter-workspace-actions.c
index 7afb6e22f..4fb0513cd 100644
--- a/src/libide/greeter/ide-greeter-workspace-actions.c
+++ b/src/libide/greeter/ide-greeter-workspace-actions.c
@@ -25,6 +25,8 @@
 #include <glib/gi18n.h>
 #include <libpeas/peas.h>
 
+#include <libide-projects.h>
+
 #include "ide-greeter-private.h"
 #include "ide-greeter-workspace.h"
 
@@ -65,7 +67,7 @@ ide_greeter_workspace_dialog_response (IdeGreeterWorkspace  *self,
       ide_greeter_workspace_open_project (self, project_info);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 }
 
 static void
@@ -108,6 +110,7 @@ ide_greeter_workspace_actions_open (GSimpleAction *action,
   GtkFileFilter *all_filter;
   const GList *list;
   gint64 last_priority = G_MAXINT64;
+  g_autoptr(GFile) projects_dir = NULL;
 
   g_assert (G_IS_SIMPLE_ACTION (action));
   g_assert (param == NULL);
@@ -232,8 +235,8 @@ ide_greeter_workspace_actions_open (GSimpleAction *action,
   if (last_priority == G_MAXINT64)
     gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), all_filter);
 
-  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
-                                       ide_get_projects_dir ());
+  projects_dir = g_file_new_for_path (ide_get_projects_dir ());
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), projects_dir, NULL);
 
   ide_gtk_window_present (GTK_WINDOW (dialog));
 }
diff --git a/src/libide/greeter/ide-greeter-workspace.c b/src/libide/greeter/ide-greeter-workspace.c
index 8fd68f96e..e08404e0c 100644
--- a/src/libide/greeter/ide-greeter-workspace.c
+++ b/src/libide/greeter/ide-greeter-workspace.c
@@ -22,13 +22,13 @@
 
 #include "config.h"
 
-#include <dazzle.h>
 #include <glib/gi18n.h>
 #include <libpeas/peas.h>
 
-#include "ide-clone-surface.h"
 #include "ide-greeter-buttons-section.h"
 #include "ide-greeter-private.h"
+#include "ide-greeter-resources.h"
+#include "ide-greeter-row.h"
 #include "ide-greeter-workspace.h"
 
 /**
@@ -36,13 +36,11 @@
  * @title: IdeGreeterWorkspace
  * @short_description: The greeter upon starting Builder
  *
- * Use the #IdeWorkspace APIs to add surfaces for user guides such
+ * Use the #IdeWorkspace APIs to add pages for user guides such
  * as the git workflow or project creation wizard.
  *
  * You can add buttons to the headerbar and use actions to change
- * surfaces such as "win.surface::'surface-name'".
- *
- * Since: 3.32
+ * pages such as "win.page::'page-name'".
  */
 
 struct _IdeGreeterWorkspace
@@ -50,26 +48,23 @@ struct _IdeGreeterWorkspace
   IdeWorkspace              parent_instance;
 
   PeasExtensionSet         *addins;
-  DzlPatternSpec           *pattern_spec;
+  IdePatternSpec           *pattern_spec;
   GSimpleAction            *delete_action;
   GSimpleAction            *purge_action;
 
   /* Template Widgets */
-  IdeCloneSurface          *clone_surface;
   IdeHeaderBar             *header_bar;
-  DzlPriorityBox           *sections;
-  DzlPriorityBox           *left_box;
-  GtkStack                 *surfaces;
-  IdeSurface               *sections_surface;
+  GtkBox                   *sections;
+  GtkBox                   *left_box;
+  GtkStack                 *pages;
   GtkSearchEntry           *search_entry;
   GtkButton                *back_button;
   GtkButton                *select_button;
   GtkActionBar             *action_bar;
   GtkActionBar             *projects_action_bar;
-  GtkLabel                 *title;
+  AdwWindowTitle           *title;
   IdeGreeterButtonsSection *buttons_section;
-  DzlEmptyState            *empty_state;
-  GtkGestureMultiPress     *multipress_gesture;
+  AdwStatusPage            *empty_state;
 
   guint                     selection_mode : 1;
 };
@@ -82,31 +77,56 @@ enum {
   N_PROPS
 };
 
+enum {
+  OPEN_PROJECT,
+  N_SIGNALS
+};
+
 static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+#define GET_PRIORITY(w)   GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w),"PRIORITY"))
+#define SET_PRIORITY(w,i) g_object_set_data(G_OBJECT(w),"PRIORITY",GINT_TO_POINTER(i))
 
 static void
-ide_greeter_workspace_has_match_cb (GtkWidget *widget,
-                                    gpointer   user_data)
+add_with_priority (GtkWidget *parent,
+                   GtkWidget *widget,
+                   int        priority)
 {
-  gboolean *match = user_data;
+  GtkWidget *sibling = NULL;
+
+  g_return_if_fail (GTK_IS_WIDGET (parent));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  if (IDE_IS_GREETER_SECTION (widget))
-    *match |= gtk_widget_get_visible (widget);
+  SET_PRIORITY (widget, priority);
+
+  for (GtkWidget *child = gtk_widget_get_first_child (parent);
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (priority < GET_PRIORITY (child))
+        break;
+      sibling = child;
+    }
+
+  gtk_widget_insert_after (widget, parent, sibling);
 }
 
 static gboolean
 ide_greeter_workspace_has_match (IdeGreeterWorkspace *self)
 {
-  gboolean match = FALSE;
-
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
 
-  gtk_container_foreach (GTK_CONTAINER (self->sections),
-                         ide_greeter_workspace_has_match_cb,
-                         &match);
+  for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->sections));
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (IDE_IS_GREETER_SECTION (child) && gtk_widget_get_visible (child))
+        return TRUE;
+    }
 
-  return match;
+  return FALSE;
 }
 
 static void
@@ -138,10 +158,10 @@ ide_greeter_workspace_apply_filter_all (IdeGreeterWorkspace *self)
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
 
-  g_clear_pointer (&self->pattern_spec, dzl_pattern_spec_unref);
+  g_clear_pointer (&self->pattern_spec, ide_pattern_spec_unref);
 
-  if (NULL != (text = gtk_entry_get_text (GTK_ENTRY (self->search_entry))))
-    self->pattern_spec = dzl_pattern_spec_new (text);
+  if (NULL != (text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry))))
+    self->pattern_spec = ide_pattern_spec_new (text);
 
   if (self->addins != NULL)
     peas_extension_set_foreach (self->addins,
@@ -152,39 +172,26 @@ ide_greeter_workspace_apply_filter_all (IdeGreeterWorkspace *self)
                           !ide_greeter_workspace_has_match (self));
 }
 
-static void
-ide_greeter_workspace_activate_cb (GtkWidget *widget,
-                                   gpointer   user_data)
-{
-  gboolean *handled = user_data;
-
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GTK_IS_WIDGET (widget));
-  g_assert (handled != NULL);
-
-  if (!IDE_IS_GREETER_SECTION (widget))
-    return;
-
-  if (!*handled)
-    *handled = ide_greeter_section_activate_first (IDE_GREETER_SECTION (widget));
-}
-
 static void
 ide_greeter_workspace_search_entry_activate (IdeGreeterWorkspace *self,
                                              GtkSearchEntry      *search_entry)
 {
-  gboolean handled = FALSE;
-
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
   g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
 
-  gtk_container_foreach (GTK_CONTAINER (self->sections),
-                         ide_greeter_workspace_activate_cb,
-                         &handled);
+  for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->sections));
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (IDE_IS_GREETER_SECTION (child))
+        {
+          if (ide_greeter_section_activate_first (IDE_GREETER_SECTION (child)))
+            return;
+        }
+    }
 
-  if (!handled)
-    gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (search_entry)));
+  gtk_widget_error_bell (GTK_WIDGET (search_entry));
 }
 
 static void
@@ -205,28 +212,30 @@ stack_notify_visible_child_cb (IdeGreeterWorkspace *self,
 {
   g_autofree gchar *title = NULL;
   g_autofree gchar *full_title = NULL;
+  GtkStackPage *page;
   GtkWidget *visible_child;
-  gboolean sections;
+  gboolean overview;
 
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
   g_assert (GTK_IS_STACK (stack));
 
   visible_child = gtk_stack_get_visible_child (stack);
+  page = gtk_stack_get_page (stack, visible_child);
 
-  if (DZL_IS_DOCK_ITEM (visible_child))
+  if (page != NULL)
     {
-      if ((title = dzl_dock_item_get_title (DZL_DOCK_ITEM (visible_child))))
+      if ((title = g_strdup (gtk_stack_page_get_title (page))))
         full_title = g_strdup_printf (_("Builder — %s"), title);
     }
 
-  gtk_label_set_label (self->title, title);
+  adw_window_title_set_title (self->title, title);
   gtk_window_set_title (GTK_WINDOW (self), full_title);
 
-  sections = ide_str_equal0 ("sections", gtk_stack_get_visible_child_name (stack));
+  overview = ide_str_equal0 ("overview", gtk_stack_get_visible_child_name (stack));
 
-  gtk_widget_set_visible (GTK_WIDGET (self->left_box), sections);
-  gtk_widget_set_visible (GTK_WIDGET (self->back_button), !sections);
-  gtk_widget_set_visible (GTK_WIDGET (self->select_button), sections);
+  gtk_widget_set_visible (GTK_WIDGET (self->left_box), overview);
+  gtk_widget_set_visible (GTK_WIDGET (self->back_button), !overview);
+  gtk_widget_set_visible (GTK_WIDGET (self->select_button), overview);
 }
 
 static void
@@ -259,13 +268,15 @@ ide_greeter_workspace_addin_removed_cb (PeasExtensionSet *set,
                                         gpointer          user_data)
 {
   IdeGreeterSection *section = (IdeGreeterSection *)exten;
+  GtkBox *box;
 
   g_assert (PEAS_IS_EXTENSION_SET (set));
   g_assert (plugin_info != NULL);
   g_assert (IDE_IS_GREETER_SECTION (section));
   g_assert (IDE_IS_GREETER_WORKSPACE (user_data));
 
-  gtk_widget_destroy (GTK_WIDGET (section));
+  box = GTK_BOX (gtk_widget_get_parent (GTK_WIDGET (section)));
+  gtk_box_remove (box, GTK_WIDGET (section));
 }
 
 static void
@@ -275,7 +286,7 @@ ide_greeter_workspace_constructed (GObject *object)
 
   G_OBJECT_CLASS (ide_greeter_workspace_parent_class)->constructed (object);
 
-  dzl_gtk_widget_add_style_class (GTK_WIDGET (self), "greeter");
+  gtk_widget_add_css_class (GTK_WIDGET (self), "greeter");
 
   self->addins = peas_extension_set_new (peas_engine_get_default (),
                                          IDE_TYPE_GREETER_SECTION,
@@ -296,11 +307,27 @@ ide_greeter_workspace_constructed (GObject *object)
                               self);
 
   /* Ensure that no plugin changed our page */
-  ide_workspace_set_visible_surface_name (IDE_WORKSPACE (self), "sections");
+  ide_greeter_workspace_set_page_name (self, "overview");
 
   gtk_widget_grab_focus (GTK_WIDGET (self->search_entry));
 }
 
+static void
+tear_workbench_down (GtkDialog    *dialog,
+                     int           response,
+                     IdeWorkbench *workbench)
+{
+  IDE_ENTRY;
+
+  g_assert (GTK_IS_DIALOG (dialog));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  gtk_window_destroy (GTK_WINDOW (dialog));
+  ide_workbench_unload_async (workbench, NULL, NULL, NULL);
+
+  IDE_EXIT;
+}
+
 static void
 ide_greeter_workspace_open_project_cb (GObject      *object,
                                        GAsyncResult *result,
@@ -331,54 +358,37 @@ ide_greeter_workspace_open_project_cb (GObject      *object,
                     "secondary-text", error->message,
                     NULL);
 
-      g_signal_connect (dialog,
-                        "response",
-                        G_CALLBACK (gtk_widget_destroy),
-                        NULL);
-      g_signal_connect_swapped (dialog,
-                                "response",
-                                G_CALLBACK (gtk_widget_destroy),
-                                workbench);
+      g_signal_connect_object (dialog,
+                               "response",
+                               G_CALLBACK (tear_workbench_down),
+                               workbench,
+                               0);
 
-      ide_gtk_window_present (GTK_WINDOW (dialog));
+      gtk_window_present (GTK_WINDOW (dialog));
 
       ide_greeter_workspace_end (self);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (self));
+  ide_workbench_remove_workspace (workbench, IDE_WORKSPACE (self));
+  gtk_window_destroy (GTK_WINDOW (self));
 
   IDE_EXIT;
 }
 
-/**
- * ide_greeter_workspace_open_project:
- * @self: an #IdeGreeterWorkspace
- * @project_info: an #IdeProjectInfo
- *
- * Opens the project described by @project_info.
- *
- * This is useful by greeter workspace extensions that add new surfaces
- * which may not have other means to activate a project.
- *
- * Since: 3.32
- */
-void
-ide_greeter_workspace_open_project (IdeGreeterWorkspace *self,
-                                    IdeProjectInfo      *project_info)
+static gboolean
+ide_greeter_workspace_real_open_project (IdeGreeterWorkspace *self,
+                                         IdeProjectInfo      *project_info)
 {
-  IdeWorkbench *workbench;
   const gchar *vcs_uri = NULL;
-  GFile *file;
 
   IDE_ENTRY;
 
-  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
-  g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+  g_assert (IDE_IS_GREETER_WORKSPACE (self));
+  g_assert (IDE_IS_PROJECT_INFO (project_info));
 
-  /* If there is a VCS Uri and no project file/directory, then we want
-   * to switch to the clone dialog. However, we can use the VCS Uri to
-   * determine what the check-out directory would be, and if so, we can
-   * just open that directory.
+  /* If there is a VCS Uri and no project file/directory, then we might
+   * be able to guess the directory based on the clone vcs uri directory
+   * name. Use that to see if we can skip cloning again.
    */
   if (!ide_project_info_get_file (project_info) &&
       !ide_project_info_get_directory (project_info) &&
@@ -395,21 +405,48 @@ ide_greeter_workspace_open_project (IdeGreeterWorkspace *self,
         {
           g_autoptr(GFile) directory = g_file_new_for_path (checkout);
           ide_project_info_set_directory (project_info, directory);
-        }
-      else
-        {
-          ide_clone_surface_set_uri (self->clone_surface, vcs_uri);
-          ide_workspace_set_visible_surface_name (IDE_WORKSPACE (self), "clone");
-          return;
+          IDE_RETURN (FALSE);
         }
     }
 
-  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+  IDE_RETURN (FALSE);
+}
+
+/**
+ * ide_greeter_workspace_open_project:
+ * @self: an #IdeGreeterWorkspace
+ * @project_info: an #IdeProjectInfo
+ *
+ * Opens the project described by @project_info.
+ *
+ * This is useful by greeter workspace extensions that add new pages
+ * which may not have other means to activate a project.
+ */
+void
+ide_greeter_workspace_open_project (IdeGreeterWorkspace *self,
+                                    IdeProjectInfo      *project_info)
+{
+  IdeWorkbench *workbench;
+  gboolean ret = FALSE;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
+  g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+
+  g_signal_emit (self, signals [OPEN_PROJECT], 0, project_info, &ret);
+
+  if (ret)
+    IDE_GOTO (not_ready);
+
+  workbench = ide_workspace_get_workbench (IDE_WORKSPACE (self));
 
   ide_greeter_workspace_begin (self);
 
   if (ide_project_info_get_directory (project_info) == NULL)
     {
+      GFile *file;
+
       if ((file = ide_project_info_get_file (project_info)))
         {
           g_autoptr(GFile) parent = g_file_get_parent (file);
@@ -429,20 +466,21 @@ ide_greeter_workspace_open_project (IdeGreeterWorkspace *self,
                                     ide_greeter_workspace_open_project_cb,
                                     g_object_ref (self));
 
+not_ready:
   IDE_EXIT;
 }
 
 static void
-ide_greeter_workspace_multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture,
-                                                     guint                 n_press,
-                                                     gdouble               x,
-                                                     gdouble               y,
-                                                     IdeGreeterWorkspace  *self)
+ide_greeter_workspace_click_pressed_cb (IdeGreeterWorkspace *self,
+                                        guint                n_press,
+                                        double               x,
+                                        double               y,
+                                        GtkGestureClick     *gesture)
 {
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
-  g_assert (GTK_IS_GESTURE_MULTI_PRESS (gesture));
+  g_assert (GTK_IS_GESTURE_CLICK (gesture));
 
-  ide_workspace_set_visible_surface_name (IDE_WORKSPACE (self), "sections");
+  ide_greeter_workspace_set_page_name (self, "overview");
 }
 
 static void
@@ -457,14 +495,6 @@ ide_greeter_workspace_project_activated_cb (IdeGreeterWorkspace *self,
   ide_greeter_workspace_open_project (self, project_info);
 }
 
-static void
-ide_greeter_workspace_delete_selected_rows_cb (GtkWidget *widget,
-                                               gpointer   user_data)
-{
-  if (IDE_IS_GREETER_SECTION (widget))
-    ide_greeter_section_delete_selected (IDE_GREETER_SECTION (widget));
-}
-
 static void
 ide_greeter_workspace_delete_selected_rows (GSimpleAction *action,
                                             GVariant      *param,
@@ -476,21 +506,18 @@ ide_greeter_workspace_delete_selected_rows (GSimpleAction *action,
   g_assert (param == NULL);
   g_assert (IDE_IS_GREETER_WORKSPACE (self));
 
-  gtk_container_foreach (GTK_CONTAINER (self->sections),
-                         ide_greeter_workspace_delete_selected_rows_cb,
-                         NULL);
+  for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->sections));
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (IDE_IS_GREETER_SECTION (child))
+        ide_greeter_section_delete_selected (IDE_GREETER_SECTION (child));
+    }
+
   ide_greeter_workspace_apply_filter_all (self);
   ide_greeter_workspace_set_selection_mode (self, FALSE);
 }
 
-static void
-ide_greeter_workspace_purge_selected_rows_cb (GtkWidget *widget,
-                                              gpointer   user_data)
-{
-  if (IDE_IS_GREETER_SECTION (widget))
-    ide_greeter_section_purge_selected (IDE_GREETER_SECTION (widget));
-}
-
 static void
 purge_selected_rows_response (IdeGreeterWorkspace *self,
                               gint                 response,
@@ -501,14 +528,19 @@ purge_selected_rows_response (IdeGreeterWorkspace *self,
 
   if (response == GTK_RESPONSE_OK)
     {
-      gtk_container_foreach (GTK_CONTAINER (self->sections),
-                             ide_greeter_workspace_purge_selected_rows_cb,
-                             NULL);
+      for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->sections));
+           child != NULL;
+           child = gtk_widget_get_next_sibling (child))
+        {
+          if (IDE_IS_GREETER_SECTION (child))
+            ide_greeter_section_purge_selected (IDE_GREETER_SECTION (child));
+        }
+
       ide_greeter_workspace_apply_filter_all (self);
       ide_greeter_workspace_set_selection_mode (self, FALSE);
     }
 
-  gtk_widget_destroy (GTK_WIDGET (dialog));
+  gtk_window_destroy (GTK_WINDOW (dialog));
 }
 
 static void
@@ -529,36 +561,52 @@ ide_greeter_workspace_purge_selected_rows (GSimpleAction *action,
   dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
                          "modal", TRUE,
                          "transient-for", parent,
-                         "attached-to", parent,
-                         "text", _("Removing project sources will delete them from your computer and cannot 
be undone."),
+                         "text", _("Delete Project Sources?"),
+                         "secondary-text", _("Deleting the project source code from your computer cannot be 
undone."),
                          NULL);
   gtk_dialog_add_buttons (dialog,
                           _("Cancel"), GTK_RESPONSE_CANCEL,
                           _("Delete Project Sources"), GTK_RESPONSE_OK,
                           NULL);
   button = gtk_dialog_get_widget_for_response (dialog, GTK_RESPONSE_OK);
-  dzl_gtk_widget_add_style_class (button, "destructive-action");
-  g_signal_connect_data (dialog,
-                         "response",
-                         G_CALLBACK (purge_selected_rows_response),
-                         g_object_ref (self),
-                         (GClosureNotify)g_object_unref,
-                         G_CONNECT_SWAPPED);
-  ide_gtk_window_present (GTK_WINDOW (dialog));
+  gtk_widget_add_css_class (button, "destructive-action");
+  g_signal_connect_object (dialog,
+                           "response",
+                           G_CALLBACK (purge_selected_rows_response),
+                           self,
+                           G_CONNECT_SWAPPED);
+  gtk_window_present (GTK_WINDOW (dialog));
 }
 
 static void
-ide_greeter_workspace_destroy (GtkWidget *widget)
+ide_greeter_workspace_page_action (GtkWidget  *widget,
+                                   const char *action_name,
+                                   GVariant   *param)
 {
   IdeGreeterWorkspace *self = (IdeGreeterWorkspace *)widget;
 
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_GREETER_WORKSPACE (self));
+  g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
+
+  ide_greeter_workspace_set_page_name (self,
+                                       g_variant_get_string (param, NULL));
+
+  IDE_EXIT;
+}
+
+static void
+ide_greeter_workspace_dispose (GObject *object)
+{
+  IdeGreeterWorkspace *self = (IdeGreeterWorkspace *)object;
+
   g_clear_object (&self->addins);
   g_clear_object (&self->delete_action);
   g_clear_object (&self->purge_action);
-  g_clear_object (&self->multipress_gesture);
-  g_clear_pointer (&self->pattern_spec, dzl_pattern_spec_unref);
+  g_clear_pointer (&self->pattern_spec, ide_pattern_spec_unref);
 
-  GTK_WIDGET_CLASS (ide_greeter_workspace_parent_class)->destroy (widget);
+  G_OBJECT_CLASS (ide_greeter_workspace_parent_class)->dispose (object);
 }
 
 static void
@@ -607,10 +655,12 @@ ide_greeter_workspace_class_init (IdeGreeterWorkspaceClass *klass)
   IdeWorkspaceClass *workspace_class = IDE_WORKSPACE_CLASS (klass);
 
   object_class->constructed = ide_greeter_workspace_constructed;
+  object_class->dispose = ide_greeter_workspace_dispose;
   object_class->get_property = ide_greeter_workspace_get_property;
   object_class->set_property = ide_greeter_workspace_set_property;
 
-  widget_class->destroy = ide_greeter_workspace_destroy;
+  workspace_class->restore_size = NULL;
+  workspace_class->save_size = NULL;
 
   /**
    * IdeGreeterWorkspace:selection-mode:
@@ -620,8 +670,6 @@ ide_greeter_workspace_class_init (IdeGreeterWorkspaceClass *klass)
    * and cached data.
    *
    * This is usually used by the checkmark button to toggle selections.
-   *
-   * Since: 3.32
    */
   properties [PROP_SELECTION_MODE] =
     g_param_spec_boolean ("selection-mode",
@@ -632,44 +680,63 @@ ide_greeter_workspace_class_init (IdeGreeterWorkspaceClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
+  signals [OPEN_PROJECT] =
+    g_signal_new_class_handler ("open-project",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_LAST,
+                                G_CALLBACK (ide_greeter_workspace_real_open_project),
+                                g_signal_accumulator_true_handled, NULL,
+                                NULL,
+                                G_TYPE_BOOLEAN, 1, IDE_TYPE_PROJECT_INFO);
+
   ide_workspace_class_set_kind (workspace_class, "greeter");
 
-  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-greeter-workspace.ui");
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/libide-greeter/ide-greeter-workspace.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, action_bar);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, back_button);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, buttons_section);
-  gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, clone_surface);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, empty_state);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, header_bar);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, left_box);
+  gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, pages);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, projects_action_bar);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, search_entry);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, sections);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, select_button);
-  gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, surfaces);
   gtk_widget_class_bind_template_child (widget_class, IdeGreeterWorkspace, title);
   gtk_widget_class_bind_template_callback (widget_class, stack_notify_visible_child_cb);
 
-  g_type_ensure (IDE_TYPE_CLONE_SURFACE);
+  gtk_widget_class_install_action (widget_class, "greeter.page", "s", ide_greeter_workspace_page_action);
+
+  gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Left, GDK_ALT_MASK, "win.page", "s", 
"overview");
+  gtk_widget_class_add_binding_action (widget_class, GDK_KEY_w, GDK_CONTROL_MASK, "window.close", NULL);
+
   g_type_ensure (IDE_TYPE_GREETER_BUTTONS_SECTION);
+  g_type_ensure (IDE_TYPE_GREETER_ROW);
+
+  g_resources_register (ide_greeter_get_resource ());
 }
 
 static void
 ide_greeter_workspace_init (IdeGreeterWorkspace *self)
 {
-  g_autoptr(GPropertyAction) selection_action = NULL;
   static const GActionEntry actions[] = {
     { "purge-selected-rows", ide_greeter_workspace_purge_selected_rows },
     { "delete-selected-rows", ide_greeter_workspace_delete_selected_rows },
   };
 
+  g_autoptr(GPropertyAction) selection_action = NULL;
+  GtkGesture *gesture;
+
   gtk_widget_init_template (GTK_WIDGET (self));
 
   selection_action = g_property_action_new ("selection-mode", G_OBJECT (self), "selection-mode");
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (selection_action));
   g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self);
-  self->multipress_gesture = GTK_GESTURE_MULTI_PRESS (gtk_gesture_multi_press_new (GTK_WIDGET (self)));
-  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->multipress_gesture), 8);
+
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 8);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
 
   g_signal_connect_object (self->search_entry,
                            "activate",
@@ -685,18 +752,18 @@ ide_greeter_workspace_init (IdeGreeterWorkspace *self)
 
   g_signal_connect (self->search_entry,
                     "stop-search",
-                    G_CALLBACK (gtk_entry_set_text),
+                    G_CALLBACK (gtk_editable_set_text),
                     (gpointer) "");
 
-  g_signal_connect (self->multipress_gesture,
-                    "pressed",
-                    G_CALLBACK (ide_greeter_workspace_multipress_gesture_pressed_cb),
-                    self);
+  g_signal_connect_object (gesture,
+                           "pressed",
+                           G_CALLBACK (ide_greeter_workspace_click_pressed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
 
-  stack_notify_visible_child_cb (self, NULL, self->surfaces);
+  stack_notify_visible_child_cb (self, NULL, self->pages);
 
   _ide_greeter_workspace_init_actions (self);
-  _ide_greeter_workspace_init_shortcuts (self);
 }
 
 IdeGreeterWorkspace *
@@ -715,8 +782,6 @@ ide_greeter_workspace_new (IdeApplication *app)
  * @section: an #IdeGreeterSection based #GtkWidget
  *
  * Adds the #IdeGreeterSection to the display.
- *
- * Since: 3.32
  */
 void
 ide_greeter_workspace_add_section (IdeGreeterWorkspace *self,
@@ -731,9 +796,9 @@ ide_greeter_workspace_add_section (IdeGreeterWorkspace *self,
                            self,
                            G_CONNECT_SWAPPED);
 
-  gtk_container_add_with_properties (GTK_CONTAINER (self->sections), GTK_WIDGET (section),
-                                     "priority", ide_greeter_section_get_priority (section),
-                                     NULL);
+  add_with_priority (GTK_WIDGET (self->sections),
+                     GTK_WIDGET (section),
+                     ide_greeter_section_get_priority (section));
 
   gtk_widget_set_visible (GTK_WIDGET (section),
                           ide_greeter_section_filter (section, NULL));
@@ -752,8 +817,6 @@ ide_greeter_workspace_add_section (IdeGreeterWorkspace *self,
  *
  * Plugins should clean up after themselves when they are unloaded, which may
  * include calling this function.
- *
- * Since: 3.32
  */
 void
 ide_greeter_workspace_remove_section (IdeGreeterWorkspace *self,
@@ -761,8 +824,9 @@ ide_greeter_workspace_remove_section (IdeGreeterWorkspace *self,
 {
   g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
   g_return_if_fail (IDE_IS_GREETER_SECTION (section));
+  g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (section)) == GTK_WIDGET (self->sections));
 
-  gtk_container_remove (GTK_CONTAINER (self->sections), GTK_WIDGET (section));
+  gtk_box_remove (self->sections, GTK_WIDGET (section));
 }
 
 void
@@ -787,8 +851,6 @@ ide_greeter_workspace_add_button (IdeGreeterWorkspace *self,
  * Actions such as switching guides will be disabled during this process.
  *
  * See ide_greeter_workspace_end() to restore actions.
- *
- * Since: 3.32
  */
 void
 ide_greeter_workspace_begin (IdeGreeterWorkspace *self)
@@ -797,12 +859,8 @@ ide_greeter_workspace_begin (IdeGreeterWorkspace *self)
 
   gtk_widget_set_sensitive (GTK_WIDGET (self->sections), FALSE);
 
-  dzl_gtk_widget_action_set (GTK_WIDGET (self), "win", "open",
-                             "enabled", FALSE,
-                             NULL);
-  dzl_gtk_widget_action_set (GTK_WIDGET (self), "win", "surface",
-                             "enabled", FALSE,
-                             NULL);
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "greeter.page", FALSE);
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "greeter.open", FALSE);
 }
 
 /**
@@ -810,20 +868,14 @@ ide_greeter_workspace_begin (IdeGreeterWorkspace *self)
  * @self: a #IdeGreeterWorkspace
  *
  * Restores actions after a call to ide_greeter_workspace_begin().
- *
- * Since: 3.32
  */
 void
 ide_greeter_workspace_end (IdeGreeterWorkspace *self)
 {
   g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
 
-  dzl_gtk_widget_action_set (GTK_WIDGET (self), "win", "open",
-                             "enabled", TRUE,
-                             NULL);
-  dzl_gtk_widget_action_set (GTK_WIDGET (self), "win", "surface",
-                             "enabled", TRUE,
-                             NULL);
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "greeter.page", TRUE);
+  gtk_widget_action_set_enabled (GTK_WIDGET (self), "greeter.open", TRUE);
 
   gtk_widget_set_sensitive (GTK_WIDGET (self->sections), TRUE);
 }
@@ -836,8 +888,6 @@ ide_greeter_workspace_end (IdeGreeterWorkspace *self)
  * allows selecting projects for removal.
  *
  * Returns: %TRUE if in selection mode, otherwise %FALSE
- *
- * Since: 3.32
  */
 gboolean
 ide_greeter_workspace_get_selection_mode (IdeGreeterWorkspace *self)
@@ -847,23 +897,12 @@ ide_greeter_workspace_get_selection_mode (IdeGreeterWorkspace *self)
   return self->selection_mode;
 }
 
-static void
-ide_greeter_workspace_set_selection_mode_cb (GtkWidget *widget,
-                                             gpointer   user_data)
-{
-  if (IDE_IS_GREETER_SECTION (widget))
-    ide_greeter_section_set_selection_mode (IDE_GREETER_SECTION (widget),
-                                            GPOINTER_TO_INT (user_data));
-}
-
 /**
  * ide_greeter_workspace_set_selection_mode:
  * @self: a #IdeGreeterWorkspace
  * @selection_mode: if the workspace should be in selection mode
  *
  * Sets the workspace in selection mode.
- *
- * Since: 3.32
  */
 void
 ide_greeter_workspace_set_selection_mode (IdeGreeterWorkspace *self,
@@ -876,11 +915,109 @@ ide_greeter_workspace_set_selection_mode (IdeGreeterWorkspace *self,
   if (selection_mode != self->selection_mode)
     {
       self->selection_mode = selection_mode;
-      gtk_container_foreach (GTK_CONTAINER (self->sections),
-                             ide_greeter_workspace_set_selection_mode_cb,
-                             GINT_TO_POINTER (selection_mode));
+
+      for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->sections));
+           child != NULL;
+           child = gtk_widget_get_next_sibling (child))
+        {
+          if (IDE_IS_GREETER_SECTION (child))
+            ide_greeter_section_set_selection_mode (IDE_GREETER_SECTION (child), selection_mode);
+        }
+
       gtk_widget_set_visible (GTK_WIDGET (self->action_bar), selection_mode);
       gtk_widget_set_visible (GTK_WIDGET (self->projects_action_bar), !selection_mode);
       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTION_MODE]);
     }
 }
+
+/**
+ * ide_greeter_workspace_get_page:
+ *
+ * Returns: (transfer none) (nullable): the current page, or %NULL if not
+ *   page has been added yet.
+ */
+GtkWidget *
+ide_greeter_workspace_get_page (IdeGreeterWorkspace *self)
+{
+  g_return_val_if_fail (IDE_IS_GREETER_WORKSPACE (self), NULL);
+
+  return gtk_stack_get_visible_child (self->pages);
+}
+
+void
+ide_greeter_workspace_set_page (IdeGreeterWorkspace *self,
+                                GtkWidget           *page)
+{
+  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
+  g_return_if_fail (!page || GTK_IS_WIDGET (page));
+
+  if (page != NULL)
+    gtk_stack_set_visible_child (self->pages, page);
+  else
+    gtk_stack_set_visible_child_name (self->pages, "overview");
+}
+
+const char *
+ide_greeter_workspace_get_page_name (IdeGreeterWorkspace *self)
+{
+  g_return_val_if_fail (IDE_IS_GREETER_WORKSPACE (self), NULL);
+
+  return gtk_stack_get_visible_child_name (self->pages);
+}
+
+void
+ide_greeter_workspace_set_page_name (IdeGreeterWorkspace *self,
+                                     const char          *name)
+{
+  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
+
+  if (name == NULL)
+    name = "overview";
+
+  gtk_stack_set_visible_child_name (self->pages, name);
+  gtk_widget_set_visible (GTK_WIDGET (self->back_button),
+                          !ide_str_equal0 (name, "overview"));
+}
+
+void
+ide_greeter_workspace_add_page (IdeGreeterWorkspace *self,
+                                GtkWidget           *page,
+                                const char          *name,
+                                const char          *title)
+{
+  GtkStackPage *child;
+
+  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
+  g_return_if_fail (GTK_IS_WIDGET (page));
+
+  child = gtk_stack_add_named (self->pages, page, name);
+  gtk_stack_page_set_title (child, title);
+}
+
+void
+ide_greeter_workspace_remove_page (IdeGreeterWorkspace *self,
+                                   GtkWidget           *page)
+{
+  g_return_if_fail (IDE_IS_GREETER_WORKSPACE (self));
+  g_return_if_fail (GTK_IS_WIDGET (page));
+
+  gtk_stack_remove (self->pages, page);
+}
+
+/**
+ * ide_greeter_workspace_get_page_named:
+ * @self: a #IdeGreeterWorkspace
+ *
+ * Gets a page that was added, by it's name.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget or %NULL
+ */
+GtkWidget *
+ide_greeter_workspace_get_page_named (IdeGreeterWorkspace *self,
+                                      const char          *page_name)
+{
+  g_return_val_if_fail (IDE_IS_GREETER_WORKSPACE (self), NULL);
+  g_return_val_if_fail (page_name != NULL, NULL);
+
+  return gtk_stack_get_child_by_name (self->pages, page_name);
+}
diff --git a/src/libide/greeter/ide-greeter-workspace.h b/src/libide/greeter/ide-greeter-workspace.h
index d3b0dd749..c31c32e59 100644
--- a/src/libide/greeter/ide-greeter-workspace.h
+++ b/src/libide/greeter/ide-greeter-workspace.h
@@ -20,6 +20,10 @@
 
 #pragma once
 
+#if !defined (IDE_GREETER_INSIDE) && !defined (IDE_GREETER_COMPILATION)
+# error "Only <libide-greeter.h> can be included directly."
+#endif
+
 #include <libide-projects.h>
 #include <libide-gui.h>
 
@@ -29,33 +33,54 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_GREETER_WORKSPACE (ide_greeter_workspace_get_type())
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (IdeGreeterWorkspace, ide_greeter_workspace, IDE, GREETER_WORKSPACE, IdeWorkspace)
 
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 IdeGreeterWorkspace *ide_greeter_workspace_new                (IdeApplication      *app);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_add_section        (IdeGreeterWorkspace *self,
                                                                IdeGreeterSection   *section);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_remove_section     (IdeGreeterWorkspace *self,
                                                                IdeGreeterSection   *section);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_add_button         (IdeGreeterWorkspace *self,
                                                                GtkWidget           *button,
                                                                gint                 priority);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_begin              (IdeGreeterWorkspace *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_end                (IdeGreeterWorkspace *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 gboolean             ide_greeter_workspace_get_selection_mode (IdeGreeterWorkspace *self);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_set_selection_mode (IdeGreeterWorkspace *self,
                                                                gboolean             selection_mode);
-IDE_AVAILABLE_IN_3_32
+IDE_AVAILABLE_IN_ALL
 void                 ide_greeter_workspace_open_project       (IdeGreeterWorkspace *self,
                                                                IdeProjectInfo      *project_info);
+IDE_AVAILABLE_IN_ALL
+void                 ide_greeter_workspace_add_page           (IdeGreeterWorkspace *self,
+                                                               GtkWidget           *widget,
+                                                               const char          *name,
+                                                               const char          *title);
+IDE_AVAILABLE_IN_ALL
+void                 ide_greeter_workspace_remove_page        (IdeGreeterWorkspace *self,
+                                                               GtkWidget           *widget);
+IDE_AVAILABLE_IN_ALL
+GtkWidget           *ide_greeter_workspace_get_page           (IdeGreeterWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+void                 ide_greeter_workspace_set_page           (IdeGreeterWorkspace *self,
+                                                               GtkWidget           *page);
+IDE_AVAILABLE_IN_ALL
+GtkWidget           *ide_greeter_workspace_get_page_named     (IdeGreeterWorkspace *self,
+                                                               const char          *page_name);
+IDE_AVAILABLE_IN_ALL
+const char          *ide_greeter_workspace_get_page_name      (IdeGreeterWorkspace *self);
+IDE_AVAILABLE_IN_ALL
+void                 ide_greeter_workspace_set_page_name      (IdeGreeterWorkspace *self,
+                                                               const char          *name);
 
 
 G_END_DECLS
diff --git a/src/libide/greeter/ide-greeter-workspace.ui b/src/libide/greeter/ide-greeter-workspace.ui
index 2857e23c4..d479e2a8e 100644
--- a/src/libide/greeter/ide-greeter-workspace.ui
+++ b/src/libide/greeter/ide-greeter-workspace.ui
@@ -1,123 +1,107 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <!-- interface-requires gtk+ 3.24 -->
+  <requires lib="gtk" version="4.0"/>
   <template class="IdeGreeterWorkspace" parent="IdeWorkspace">
     <child type="titlebar">
       <object class="IdeHeaderBar" id="header_bar">
+        <property name="flat">true</property>
         <property name="menu-id">ide-greeter-workspace-menu</property>
-        <property name="show-fullscreen-button">false</property>
-        <property name="show-close-button">true</property>
-        <property name="visible">true</property>
-        <child type="title">
-          <object class="GtkLabel" id="title">
-            <property name="visible">true</property>
-            <property name="hexpand">true</property>
-            <style>
-              <class name="title"/>
-            </style>
+        <child internal-child="headerbar">
+          <object class="AdwHeaderBar">
           </object>
         </child>
+        <child type="title">
+          <object class="AdwWindowTitle" id="title"/>
+        </child>
         <child type="left">
           <object class="GtkButton" id="back_button">
-            <property name="action-name">win.surface</property>
-            <property name="action-target">'sections'</property>
+            <property name="action-name">greeter.page</property>
+            <property name="action-target">'overview'</property>
             <property name="has-tooltip">true</property>
             <property name="tooltip-text" translatable="yes">Go back</property>
             <property name="margin-end">6</property>
-            <style>
-              <class name="image-button"/>
-            </style>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">true</property>
-                <property name="icon-name">go-previous-symbolic</property>
-              </object>
-            </child>
+            <property name="icon-name">go-previous-symbolic</property>
           </object>
         </child>
         <child type="left">
-          <object class="DzlPriorityBox" id="left_box">
+          <object class="GtkBox" id="left_box">
             <property name="spacing">6</property>
             <property name="hexpand">false</property>
             <property name="homogeneous">true</property>
-            <property name="visible">true</property>
           </object>
-          <packing>
-            <property name="pack-type">start</property>
-          </packing>
         </child>
         <child type="right">
           <object class="GtkToggleButton" id="select_button">
             <property name="action-name">win.selection-mode</property>
-            <property name="visible">true</property>
             <style>
               <class name="image-button"/>
             </style>
             <child>
               <object class="GtkImage">
-                <property name="icon-name">object-select-symbolic</property>
-                <property name="visible">true</property>
+                <property name="icon-name">selection-mode-symbolic</property>
               </object>
             </child>
           </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
         </child>
       </object>
     </child>
-    <child internal-child="surfaces">
-      <object class="GtkStack" id="surfaces">
+    <child>
+      <object class="GtkStack" id="pages">
         <property name="transition-type">crossfade</property>
+        <property name="hhomogeneous">false</property>
+        <property name="vhomogeneous">false</property>
         <signal name="notify::visible-child" handler="stack_notify_visible_child_cb" 
object="IdeGreeterWorkspace" swapped="true"/>
         <child>
-          <object class="IdeSurface" id="sections_surface">
+          <object class="GtkStackPage">
+            <property name="name">overview</property>
             <property name="title" translatable="yes" context="title">Open a Project</property>
-            <property name="visible">true</property>
-            <style>
-              <class name="sectionssurface"/>
-            </style>
-            <child>
+            <property name="child">
               <object class="GtkBox">
                 <property name="orientation">vertical</property>
-                <property name="visible">true</property>
                 <child>
-                  <object class="GtkScrolledWindow">
-                    <property name="expand">true</property>
+                  <object class="GtkScrolledWindow" id="scroller">
+                    <style>
+                      <class name="shadow-when-scroll"/>
+                    </style>
+                    <property name="vexpand">true</property>
                     <property name="hscrollbar-policy">never</property>
-                    <property name="visible">true</property>
                     <child>
                       <object class="GtkViewport">
-                        <property name="expand">true</property>
-                        <property name="visible">true</property>
+                        <property name="vexpand">true</property>
                         <child>
                           <object class="GtkBox">
-                            <property name="margin">32</property>
+                            <property name="margin-top">32</property>
+                            <property name="margin-start">32</property>
+                            <property name="margin-end">32</property>
+                            <property name="margin-bottom">32</property>
                             <property name="orientation">vertical</property>
                             <property name="spacing">24</property>
-                            <property name="visible">true</property>
                             <child>
                               <object class="GtkSearchEntry" id="search_entry">
                                 <property name="placeholder-text" translatable="yes">Search all Builder 
projects…</property>
                                 <property name="halign">center</property>
-                                <property name="visible">true</property>
                                 <property name="width-chars">45</property>
                               </object>
                             </child>
                             <child>
-                              <object class="DzlPriorityBox" id="sections">
+                              <object class="AdwClamp">
+                                <property name="halign">center</property>
+                                <property name="maximum-size">600</property>
                                 <property name="orientation">vertical</property>
-                                <property name="spacing">32</property>
-                                <property name="visible">true</property>
+                                <child>
+                                  <object class="GtkBox" id="sections">
+                                    <property name="width-request">600</property>
+                                    <property name="orientation">vertical</property>
+                                    <property name="spacing">32</property>
+                                  </object>
+                                </child>
                               </object>
                             </child>
                             <child>
-                              <object class="DzlEmptyState" id="empty_state">
+                              <object class="AdwStatusPage" id="empty_state">
                                 <property name="icon-name">edit-find-symbolic</property>
-                                <property name="pixel-size">64</property>
                                 <property name="title" translatable="yes">No Projects Found</property>
                                 <property name="vexpand">true</property>
-                                <property name="valign">center</property>
                               </object>
                             </child>
                           </object>
@@ -128,32 +112,29 @@
                 </child>
                 <child>
                   <object class="GtkActionBar" id="projects_action_bar">
-                    <property name="visible">true</property>
-                    <child>
+                    <child type="center">
                       <object class="IdeGreeterButtonsSection" id="buttons_section">
                         <property name="halign">center</property>
-                        <property name="visible">true</property>
                       </object>
                     </child>
                   </object>
                 </child>
                 <child>
                   <object class="GtkActionBar" id="action_bar">
+                    <property name="visible">false</property>
                     <child type="center">
                       <object class="GtkBox">
                         <property name="width-request">600</property>
                         <property name="orientation">horizontal</property>
                         <property name="homogeneous">true</property>
-                        <property name="margin-bottom">6</property>
-                        <property name="margin-top">6</property>
+                        <property name="margin-bottom">3</property>
+                        <property name="margin-top">3</property>
                         <property name="spacing">10</property>
-                        <property name="visible">true</property>
                         <child>
                           <object class="GtkButton" id="remove_button">
                             <property name="action-name">win.delete-selected-rows</property>
                             <property name="label" translatable="yes">_Remove Projects</property>
                             <property name="use-underline">true</property>
-                            <property name="visible">true</property>
                             <property name="sensitive">false</property>
                             <style>
                               <class name="destructive-action"/>
@@ -165,7 +146,6 @@
                             <property name="action-name">win.purge-selected-rows</property>
                             <property name="label" translatable="yes">Remove Projects and Sources…</property>
                             <property name="use-underline">true</property>
-                            <property name="visible">true</property>
                             <property name="sensitive">false</property>
                             <style>
                               <class name="destructive-action"/>
@@ -177,20 +157,8 @@
                   </object>
                 </child>
               </object>
-            </child>
-          </object>
-          <packing>
-            <property name="name">sections</property>
-          </packing>
-        </child>
-        <child>
-          <object class="IdeCloneSurface" id="clone_surface">
-            <property name="title" translatable="yes" context="title">Clone Repository</property>
-            <property name="visible">true</property>
+            </property>
           </object>
-          <packing>
-            <property name="name">clone</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/src/libide/greeter/libide-greeter.gresource.xml b/src/libide/greeter/libide-greeter.gresource.xml
index ee2c4b10e..d4b17aec3 100644
--- a/src/libide/greeter/libide-greeter.gresource.xml
+++ b/src/libide/greeter/libide-greeter.gresource.xml
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
-  <gresource prefix="/org/gnome/builder/ui">
-    <file preprocess="xml-stripblanks">ide-clone-surface.ui</file>
+  <gresource prefix="/org/gnome/libide-greeter/">
     <file preprocess="xml-stripblanks">ide-greeter-row.ui</file>
     <file preprocess="xml-stripblanks">ide-greeter-workspace.ui</file>
+    <file preprocess="xml-stripblanks">gtk/menus.ui</file>
+    <file>style.css</file>
   </gresource>
 </gresources>
diff --git a/src/libide/greeter/libide-greeter.h b/src/libide/greeter/libide-greeter.h
index 0f3bc1a97..e3a3c1ca7 100644
--- a/src/libide/greeter/libide-greeter.h
+++ b/src/libide/greeter/libide-greeter.h
@@ -27,7 +27,6 @@
 
 #define IDE_GREETER_INSIDE
 
-#include "ide-clone-surface.h"
 #include "ide-greeter-row.h"
 #include "ide-greeter-section.h"
 #include "ide-greeter-workspace.h"
diff --git a/src/libide/greeter/meson.build b/src/libide/greeter/meson.build
index 121d498d0..c2be40390 100644
--- a/src/libide/greeter/meson.build
+++ b/src/libide/greeter/meson.build
@@ -8,7 +8,6 @@ libide_greeter_generated_headers = []
 #
 
 libide_greeter_public_headers = [
-  'ide-clone-surface.h',
   'ide-greeter-row.h',
   'ide-greeter-section.h',
   'ide-greeter-workspace.h',
@@ -18,7 +17,6 @@ libide_greeter_public_headers = [
 libide_greeter_private_headers = [
   'ide-greeter-buttons-section.h',
   'ide-greeter-private.h',
-  'ide-truncate-model.h',
 ]
 
 install_headers(libide_greeter_public_headers, subdir: libide_greeter_header_subdir)
@@ -28,7 +26,6 @@ install_headers(libide_greeter_public_headers, subdir: libide_greeter_header_sub
 #
 
 libide_greeter_public_sources = [
-  'ide-clone-surface.c',
   'ide-greeter-row.c',
   'ide-greeter-section.c',
   'ide-greeter-workspace.c',
@@ -36,9 +33,7 @@ libide_greeter_public_sources = [
 
 libide_greeter_private_sources = [
   'ide-greeter-workspace-actions.c',
-  'ide-greeter-workspace-shortcuts.c',
   'ide-greeter-buttons-section.c',
-  'ide-truncate-model.c',
 ]
 
 #
@@ -61,13 +56,13 @@ libide_greeter_private_sources += libide_greeter_resources
 libide_greeter_deps = [
   libgio_dep,
   libgtk_dep,
-  libdazzle_dep,
 
   libide_core_dep,
+  libide_gtk_dep,
   libide_gui_dep,
   libide_io_dep,
+  libide_search_dep,
   libide_threading_dep,
-  libide_vcs_dep,
 ]
 
 #


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