[console/wip/exalm/gtk4: 1/4] Port to GTK4 and libadwaita



commit c1f39d38f4dc9dc7a3fd3ce4b63db17bf749daa1
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Jul 18 03:29:29 2022 +0400

    Port to GTK4 and libadwaita

 .gitlab-ci.yml                                 |   4 +-
 build-aux/flatpak/org.gnome.Console.Devel.json |   4 +-
 meson.build                                    |  14 +-
 src/help-overlay.ui                            |  15 +-
 src/kgx-application.c                          |  51 +-
 src/kgx-application.h                          |   4 +-
 src/kgx-close-dialog.c                         |   8 +-
 src/kgx-close-dialog.ui                        |   6 +-
 src/kgx-pages.c                                | 170 +++----
 src/kgx-pages.h                                |   9 +-
 src/kgx-pages.ui                               |  19 +-
 src/kgx-simple-tab.ui                          |  10 +-
 src/kgx-tab-button.c                           |  30 +-
 src/kgx-tab-button.h                           |   6 +-
 src/kgx-tab-button.ui                          |  16 +-
 src/kgx-tab-switcher-row.c                     |  55 ++-
 src/kgx-tab-switcher-row.h                     |   8 +-
 src/kgx-tab-switcher-row.ui                    |  52 +--
 src/kgx-tab-switcher.c                         | 379 +++++++--------
 src/kgx-tab-switcher.h                         |  12 +-
 src/kgx-tab-switcher.ui                        |  41 +-
 src/kgx-tab.c                                  | 186 +++-----
 src/kgx-tab.h                                  |   4 +-
 src/kgx-tab.ui                                 |  60 +--
 src/kgx-terminal.c                             | 318 +++++++------
 src/kgx-terminal.h                             |   8 +-
 src/kgx-theme-switcher.c                       |  34 +-
 src/kgx-theme-switcher.h                       |   2 +-
 src/kgx-theme-switcher.ui                      |  25 +-
 src/kgx-window.c                               | 184 +++-----
 src/kgx-window.h                               |  12 +-
 src/kgx-window.ui                              | 253 ++++------
 src/kgx.gresource.xml.in                       |   2 -
 src/menus.ui                                   |   2 +-
 src/meson.build                                |  13 +-
 src/styles/_definitions.scss                   |  31 --
 src/styles/_drawing.scss                       | 614 -------------------------
 src/styles/_recoloring.scss                    | 205 ---------
 src/styles/_styles.scss                        | 185 +++-----
 src/styles/meson.build                         |   5 -
 src/styles/styles-hc-dark.scss                 |   4 -
 src/styles/styles-hc.scss                      |   4 -
 subprojects/adwaita.wrap                       |   4 +
 subprojects/handy.wrap                         |   4 -
 44 files changed, 954 insertions(+), 2118 deletions(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fd11e0e..4a19889 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,7 +5,7 @@ debian:
   stage: build
   before_script:
     - apt -y update
-    - apt -y install gcc g++ gettext git libgnutls28-dev libgtk-3-dev
+    - apt -y install gcc g++ gettext git libgnutls28-dev libgtk-4-dev
       libsystemd-dev libgtop2-dev meson sassc desktop-file-utils appstream-util
       gsettings-desktop-schemas-dev
   script:
@@ -18,7 +18,7 @@ fedora:
   before_script:
     - dnf update -y --nogpgcheck
     - dnf install -y --nogpgcheck meson gettext glib2-devel gcc gcc-c++ git
-      desktop-file-utils gperf "pkgconfig(gtk+-3.0)" libappstream-glib
+      desktop-file-utils gperf "pkgconfig(gtk4)" libappstream-glib
       "pkgconfig(vte-2.91)" "pkgconfig(libgtop-2.0)" "pkgconfig(fribidi)"
       "pkgconfig(gnutls)" "pkgconfig(libsystemd)" "pkgconfig(libpcre2-8)"
       "pkgconfig(gsettings-desktop-schemas)" sassc
diff --git a/build-aux/flatpak/org.gnome.Console.Devel.json b/build-aux/flatpak/org.gnome.Console.Devel.json
index 7ee5bca..cfdc60b 100644
--- a/build-aux/flatpak/org.gnome.Console.Devel.json
+++ b/build-aux/flatpak/org.gnome.Console.Devel.json
@@ -27,7 +27,9 @@
         {
             "name" : "vte",
             "config-opts" : [
-                "-Dvapi=false"
+                "-Dvapi=false",
+                "-Dgtk3=false",
+                "-Dgtk4=true"
             ],
             "buildsystem" : "meson",
             "sources" : [
diff --git a/meson.build b/meson.build
index 03fd02e..65da618 100644
--- a/meson.build
+++ b/meson.build
@@ -10,10 +10,12 @@ project('gnome-console', 'c', version: '42.beta',
     'vte:gir=false',
     'vte:vapi=false',
     'libsass:werror=false',
-    'handy:werror=false',
-    'handy:vapi=false',
-    'handy:introspection=disabled',
-    'handy:tests=false',
+    'vte:gtk3=false',
+    'vte:gtk4=true',
+    'adwaita:werror=false',
+    'adwaita:vapi=false',
+    'adwaita:introspection=disabled',
+    'adwaita:tests=false',
   ],
 )
 
@@ -63,11 +65,9 @@ config_h = vcs_tag( input: config_h_in,
                   command: ['git', 'rev-parse', '--short', 'HEAD'],
                  fallback: 'Devel')
 
-gtk_ver = 'GDK_VERSION_3_24'
+gtk_ver = 'GDK_VERSION_4_0'
 glib_ver = 'GLIB_VERSION_2_66'
 add_project_arguments([
-  '-DGDK_VERSION_MIN_REQUIRED=@0@'.format(gtk_ver),
-  '-DGDK_VERSION_MAX_ALLOWED=@0@'.format(gtk_ver),
   '-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_ver),
   '-DGLIB_VERSION_MAX_ALLOWED=@0@'.format(glib_ver),
   '-DG_LOG_USE_STRUCTURED',
diff --git a/src/help-overlay.ui b/src/help-overlay.ui
index 0c78737..451bfdc 100644
--- a/src/help-overlay.ui
+++ b/src/help-overlay.ui
@@ -1,20 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gtk" version="4.0"/>
   <object class="GtkShortcutsWindow" id="help_overlay">
     <property name="modal">True</property>
     <child>
       <object class="GtkShortcutsSection">
         <property name="section-name">shortcuts</property>
         <property name="max-height">12</property>
-        <property name="visible">True</property>
         <child>
           <object class="GtkShortcutsGroup">
             <property name="title" translatable="yes" context="shortcut window">Application</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">win.new-window</property>
                 <property name="title" translatable="yes" context="shortcut window">New Window</property>
               </object>
@@ -24,24 +21,20 @@
         <child>
           <object class="GtkShortcutsGroup">
             <property name="title" translatable="yes" context="shortcut window">Terminal</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">win.find</property>
                 <property name="title" translatable="yes" context="shortcut window">Find</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">term.copy</property>
                 <property name="title" translatable="yes" context="shortcut window">Copy</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">term.paste</property>
                 <property name="title" translatable="yes" context="shortcut window">Paste</property>
               </object>
@@ -51,38 +44,32 @@
         <child>
           <object class="GtkShortcutsGroup">
             <property name="title" translatable="yes" context="shortcut window">Tabs</property>
-            <property name="visible">True</property>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">win.new-tab</property>
                 <property name="title" translatable="yes" context="shortcut window">New Tab</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="action-name">win.close-tab</property>
                 <property name="title" translatable="yes" context="shortcut window">Close Tab</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="accelerator">&lt;primary&gt;Tab</property>
                 <property name="title" translatable="yes" context="shortcut window">Next Tab</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="accelerator">&lt;shift&gt;&lt;primary&gt;Tab</property>
                 <property name="title" translatable="yes" context="shortcut window">Previous Tab</property>
               </object>
             </child>
             <child>
               <object class="GtkShortcutsShortcut">
-                <property name="visible">True</property>
                 <property name="accelerator">&lt;alt&gt;1...9</property>
                 <property name="title" translatable="yes" context="shortcut window">Switch to Tab</property>
               </object>
diff --git a/src/kgx-application.c b/src/kgx-application.c
index 753f37e..0c5cd92 100644
--- a/src/kgx-application.c
+++ b/src/kgx-application.c
@@ -44,7 +44,7 @@
 #define LOGO_COL_SIZE 28
 #define LOGO_ROW_SIZE 14
 
-G_DEFINE_TYPE (KgxApplication, kgx_application, GTK_TYPE_APPLICATION)
+G_DEFINE_TYPE (KgxApplication, kgx_application, ADW_TYPE_APPLICATION)
 
 enum {
   PROP_0,
@@ -62,25 +62,25 @@ static void
 kgx_application_set_theme (KgxApplication *self,
                            KgxTheme        theme)
 {
-  HdyStyleManager *style_manager;
+  AdwStyleManager *style_manager;
 
   g_return_if_fail (KGX_IS_APPLICATION (self));
 
   self->theme = theme;
 
-  style_manager = hdy_style_manager_get_default ();
+  style_manager = adw_style_manager_get_default ();
 
   switch (theme) {
     case KGX_THEME_AUTO:
-      hdy_style_manager_set_color_scheme (style_manager, HDY_COLOR_SCHEME_PREFER_LIGHT);
+      adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_PREFER_LIGHT);
       break;
     case KGX_THEME_DAY:
-      hdy_style_manager_set_color_scheme (style_manager, HDY_COLOR_SCHEME_FORCE_LIGHT);
+      adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_FORCE_LIGHT);
       break;
     case KGX_THEME_NIGHT:
     case KGX_THEME_HACKER:
     default:
-      hdy_style_manager_set_color_scheme (style_manager, HDY_COLOR_SCHEME_FORCE_DARK);
+      adw_style_manager_set_color_scheme (style_manager, ADW_COLOR_SCHEME_FORCE_DARK);
       break;
   }
 
@@ -317,15 +317,10 @@ set_watcher (KgxApplication *self, gboolean focused)
 static void
 update_styles (KgxApplication *self)
 {
-  HdyStyleManager *style_manager = hdy_style_manager_get_default ();
-  gboolean dark = hdy_style_manager_get_dark (style_manager);
-  gboolean hc = hdy_style_manager_get_high_contrast (style_manager);
-
-  if (hc && dark) {
-    gtk_css_provider_load_from_resource (self->provider, KGX_APPLICATION_PATH "styles-hc-dark.css");
-  } else if (hc) {
-    gtk_css_provider_load_from_resource (self->provider, KGX_APPLICATION_PATH "styles-hc.css");
-  } else if (dark) {
+  AdwStyleManager *style_manager = adw_style_manager_get_default ();
+  gboolean dark = adw_style_manager_get_dark (style_manager);
+
+  if (dark) {
     gtk_css_provider_load_from_resource (self->provider, KGX_APPLICATION_PATH "styles-dark.css");
   } else {
     gtk_css_provider_load_from_resource (self->provider, KGX_APPLICATION_PATH "styles-light.css");
@@ -337,7 +332,7 @@ static void
 kgx_application_startup (GApplication *app)
 {
   KgxApplication    *self = KGX_APPLICATION (app);
-  HdyStyleManager   *style_manager;
+  AdwStyleManager   *style_manager;
   g_autoptr (GAction) settings_action = NULL;
 
   const char *const new_window_accels[] = { "<shift><primary>n", NULL };
@@ -359,8 +354,6 @@ kgx_application_startup (GApplication *app)
 
   G_APPLICATION_CLASS (kgx_application_parent_class)->startup (app);
 
-  hdy_init ();
-
   gtk_application_set_accels_for_action (GTK_APPLICATION (app),
                                          "win.new-window", new_window_accels);
   gtk_application_set_accels_for_action (GTK_APPLICATION (app),
@@ -389,14 +382,14 @@ kgx_application_startup (GApplication *app)
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (settings_action));
 
   self->provider = gtk_css_provider_new ();
-  gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
-                                             GTK_STYLE_PROVIDER (self->provider),
-                                             /* Is this stupid? Yes
-                                              * Does it fix vte using the wrong
-                                              * priority for fallback styles? Yes*/
-                                             GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
-
-  style_manager = hdy_style_manager_get_default ();
+  gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                              GTK_STYLE_PROVIDER (self->provider),
+                                              /* Is this stupid? Yes
+                                               * Does it fix vte using the wrong
+                                               * priority for fallback styles? Yes*/
+                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
+
+  style_manager = adw_style_manager_get_default ();
   g_signal_connect_swapped (style_manager, "notify::dark", G_CALLBACK (update_styles), self);
   g_signal_connect_swapped (style_manager, "notify::high-contrast", G_CALLBACK (update_styles), self);
   update_styles (self);
@@ -872,16 +865,16 @@ focus_activated (GSimpleAction *action,
                  gpointer       data)
 {
   KgxApplication *self = KGX_APPLICATION (data);
-  GtkWidget *window;
+  GtkRoot *root;
   KgxPages *pages;
   KgxTab *page;
 
   page = kgx_application_lookup_page (self, g_variant_get_uint32 (parameter));
   pages = kgx_tab_get_pages (page);
   kgx_pages_focus_page (pages, page);
-  window = gtk_widget_get_toplevel (GTK_WIDGET (pages));
+  root = gtk_widget_get_root (GTK_WIDGET (pages));
 
-  gtk_window_present_with_time (GTK_WINDOW (window), GDK_CURRENT_TIME);
+  gtk_window_present_with_time (GTK_WINDOW (root), GDK_CURRENT_TIME);
 }
 
 
diff --git a/src/kgx-application.h b/src/kgx-application.h
index 5b3162e..cfcf7e7 100644
--- a/src/kgx-application.h
+++ b/src/kgx-application.h
@@ -77,7 +77,7 @@ struct ProcessWatch {
 struct _KgxApplication
 {
   /*< private >*/
-  GtkApplication            parent_instance;
+  AdwApplication            parent_instance;
 
   /*< public >*/
   KgxTheme                  theme;
@@ -97,7 +97,7 @@ struct _KgxApplication
   GtkCssProvider           *provider;
 };
 
-G_DECLARE_FINAL_TYPE (KgxApplication, kgx_application, KGX, APPLICATION, GtkApplication)
+G_DECLARE_FINAL_TYPE (KgxApplication, kgx_application, KGX, APPLICATION, AdwApplication)
 
 
 void                  kgx_application_add_watch       (KgxApplication *self,
diff --git a/src/kgx-close-dialog.c b/src/kgx-close-dialog.c
index 16f0359..375e181 100644
--- a/src/kgx-close-dialog.c
+++ b/src/kgx-close-dialog.c
@@ -30,7 +30,7 @@
 #include "kgx-config.h"
 #include "kgx-close-dialog.h"
 #include "kgx-process.h"
-#include <handy.h>
+#include <adwaita.h>
 
 GtkWidget *
 kgx_close_dialog_new (KgxCloseDialogContext  context,
@@ -65,13 +65,11 @@ kgx_close_dialog_new (KgxCloseDialogContext  context,
     KgxProcess *process = g_ptr_array_index (commands, i);
     GtkWidget *row;
 
-    row = g_object_new (HDY_TYPE_ACTION_ROW,
-                        "visible", TRUE,
-                        "can-focus", FALSE,
+    row = g_object_new (ADW_TYPE_ACTION_ROW,
                         "title", kgx_process_get_exec (process),
                         NULL);
 
-    gtk_container_add (GTK_CONTAINER (list), row);
+    gtk_list_box_append (GTK_LIST_BOX (list), row);
   }
 
   return dialog;
diff --git a/src/kgx-close-dialog.ui b/src/kgx-close-dialog.ui
index 0f07381..7c9c501 100644
--- a/src/kgx-close-dialog.ui
+++ b/src/kgx-close-dialog.ui
@@ -1,13 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gtk" version="4.0"/>
   <object class="GtkMessageDialog" id="dialog">
     <property name="modal">True</property>
     <child internal-child="message_area">
       <object class="GtkBox">
         <child>
           <object class="GtkListBox" id="list">
-            <property name="visible">True</property>
             <property name="can-focus">False</property>
             <property name="selection-mode">none</property>
             <property name="width-request">300</property>
@@ -22,15 +21,12 @@
     <child type="action">
       <object class="GtkButton" id="button-cancel">
         <property name="label" translatable="yes">_Cancel</property>
-        <property name="visible">True</property>
-        <property name="can-default">True</property>
         <property name="use-underline">True</property>
       </object>
     </child>
     <child type="action">
       <object class="GtkButton" id="button-ok">
         <property name="label" translatable="yes">C_lose</property>
-        <property name="visible">True</property>
         <property name="use-underline">True</property>
         <style>
           <class name="destructive-action"/>
diff --git a/src/kgx-pages.c b/src/kgx-pages.c
index da7db1e..930fc59 100644
--- a/src/kgx-pages.c
+++ b/src/kgx-pages.c
@@ -21,7 +21,7 @@
  * @title: KgxPages
  * @short_description: Container of #KgxTab s
  *
- * The container of open #KgxTab (uses #HdyTabView internally)
+ * The container of open #KgxTab (uses #AdwTabView internally)
  */
 
 #include <glib/gi18n.h>
@@ -69,11 +69,11 @@ struct _KgxPagesPrivate {
   gboolean              opaque;
   gint64                scrollback_lines;
 
-  HdyTabPage           *action_page;
+  AdwTabPage           *action_page;
 };
 
 
-G_DEFINE_TYPE_WITH_PRIVATE (KgxPages, kgx_pages, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_PRIVATE (KgxPages, kgx_pages, ADW_TYPE_BIN)
 
 
 enum {
@@ -134,7 +134,7 @@ kgx_pages_get_property (GObject    *object,
 
   switch (property_id) {
     case PROP_TAB_COUNT:
-      g_value_set_uint (value, hdy_tab_view_get_n_pages (HDY_TAB_VIEW (priv->view)));
+      g_value_set_uint (value, adw_tab_view_get_n_pages (ADW_TAB_VIEW (priv->view)));
       break;
     case PROP_TAB_VIEW:
       g_value_set_object (value, priv->view);
@@ -260,7 +260,7 @@ size_changed (KgxTab   *tab,
   priv->last_cols = cols;
   priv->last_rows = rows;
 
-  if (gtk_window_is_maximized (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))))) {
+  if (gtk_window_is_maximized (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))))) {
     // Don't show when maximised as it isn't very interesting
     return;
   }
@@ -281,17 +281,17 @@ static void
 page_changed (GObject *object, GParamSpec *pspec, KgxPages *self)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *page = NULL;
+  AdwTabPage *page = NULL;
   KgxTab *tab;
 
   priv = kgx_pages_get_instance_private (self);
-  page = hdy_tab_view_get_selected_page (HDY_TAB_VIEW (priv->view));
+  page = adw_tab_view_get_selected_page (ADW_TAB_VIEW (priv->view));
 
   if (!page) {
     return;
   }
 
-  tab = KGX_TAB (hdy_tab_page_get_child (page));
+  tab = KGX_TAB (adw_tab_page_get_child (page));
 
   g_clear_signal_handler (&priv->size_watcher, priv->active_page);
   priv->size_watcher = g_signal_connect (tab,
@@ -339,12 +339,12 @@ died (KgxTab         *page,
       KgxPages       *self)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *tab_page;
+  AdwTabPage *tab_page;
   gboolean close_on_quit;
   int tab_count;
 
   priv = kgx_pages_get_instance_private (self);
-  tab_page = hdy_tab_view_get_page (HDY_TAB_VIEW (priv->view), GTK_WIDGET (page));
+  tab_page = adw_tab_view_get_page (ADW_TAB_VIEW (priv->view), GTK_WIDGET (page));
 
   g_object_get (page, "close-on-quit", &close_on_quit, NULL);
 
@@ -358,7 +358,7 @@ died (KgxTab         *page,
     return;
   }
 
-  hdy_tab_view_close_page (HDY_TAB_VIEW (priv->view), tab_page);
+  adw_tab_view_close_page (ADW_TAB_VIEW (priv->view), tab_page);
 }
 
 
@@ -372,16 +372,16 @@ zoom (KgxTab   *tab,
 
 
 static void
-page_attached (HdyTabView *view,
-               HdyTabPage *page,
+page_attached (AdwTabView *view,
+               AdwTabPage *page,
                int         position,
                KgxPages   *self)
 {
   KgxTab *tab;
 
-  g_return_if_fail (HDY_IS_TAB_PAGE (page));
+  g_return_if_fail (ADW_IS_TAB_PAGE (page));
 
-  tab = KGX_TAB (hdy_tab_page_get_child (page));
+  tab = KGX_TAB (adw_tab_page_get_child (page));
 
   g_object_connect (tab,
                     "signal::died", G_CALLBACK (died), self,
@@ -393,18 +393,18 @@ page_attached (HdyTabView *view,
 
 
 static void
-page_detached (HdyTabView *view,
-               HdyTabPage *page,
+page_detached (AdwTabView *view,
+               AdwTabPage *page,
                int         position,
                KgxPages   *self)
 {
   KgxTab *tab;
   KgxPagesPrivate *priv;
-  GtkWidget *toplevel;
+  GtkRoot *root;
 
-  g_return_if_fail (HDY_IS_TAB_PAGE (page));
+  g_return_if_fail (ADW_IS_TAB_PAGE (page));
 
-  tab = KGX_TAB (hdy_tab_page_get_child (page));
+  tab = KGX_TAB (adw_tab_page_get_child (page));
 
   priv = kgx_pages_get_instance_private (self);
 
@@ -412,12 +412,12 @@ page_detached (HdyTabView *view,
 
   g_signal_handlers_disconnect_by_data (tab, self);
 
-  if (hdy_tab_view_get_n_pages (HDY_TAB_VIEW (priv->view)) == 0) {
-    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+  if (adw_tab_view_get_n_pages (ADW_TAB_VIEW (priv->view)) == 0) {
+    root = gtk_widget_get_root (GTK_WIDGET (self));
 
-    if (GTK_IS_WINDOW (toplevel)) {
+    if (GTK_IS_WINDOW (root)) {
       /* Not a massive fan, would prefer it if window observed pages is empty */
-      gtk_window_close (GTK_WINDOW (toplevel));
+      gtk_window_close (GTK_WINDOW (root));
     }
 
     priv->active_page = NULL;
@@ -426,8 +426,8 @@ page_detached (HdyTabView *view,
 }
 
 
-static HdyTabView *
-create_window (HdyTabView *view,
+static AdwTabView *
+create_window (AdwTabView *view,
                KgxPages   *self)
 {
   /* Perhaps this should be handled via KgxWindow? */
@@ -438,7 +438,7 @@ create_window (HdyTabView *view,
   KgxPagesPrivate *priv;
   int width, height;
 
-  window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+  window = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
   app = gtk_window_get_application (window);
 
   kgx_window_get_size (KGX_WINDOW (window), &width, &height);
@@ -452,49 +452,48 @@ create_window (HdyTabView *view,
   new_pages = kgx_window_get_pages (new_window);
   priv = kgx_pages_get_instance_private (new_pages);
 
-  gtk_window_set_position (GTK_WINDOW (new_window), GTK_WIN_POS_MOUSE);
   gtk_window_present (GTK_WINDOW (new_window));
 
-  return HDY_TAB_VIEW (priv->view);
+  return ADW_TAB_VIEW (priv->view);
 }
 
 
 static void
 close_response (GtkWidget  *dlg,
                 int         response,
-                HdyTabPage *page)
+                AdwTabPage *page)
 {
-  KgxTab *tab = KGX_TAB (hdy_tab_page_get_child (page));
+  KgxTab *tab = KGX_TAB (adw_tab_page_get_child (page));
   KgxPages *self = kgx_tab_get_pages (tab);
   KgxPagesPrivate *priv = kgx_pages_get_instance_private (self);
 
-  gtk_widget_destroy (dlg);
+  gtk_window_destroy (GTK_WINDOW (dlg));
 
-  hdy_tab_view_close_page_finish (HDY_TAB_VIEW (priv->view), page,
+  adw_tab_view_close_page_finish (ADW_TAB_VIEW (priv->view), page,
                                   response == GTK_RESPONSE_OK);
 }
 
 
 static gboolean
-close_page (HdyTabView *view,
-            HdyTabPage *page,
+close_page (AdwTabView *view,
+            AdwTabPage *page,
             KgxPages   *self)
 {
   GtkWidget *dlg;
   g_autoptr (GPtrArray) children = NULL;
-  GtkWidget *toplevel;
+  GtkRoot *root;
 
-  children = kgx_tab_get_children (KGX_TAB (hdy_tab_page_get_child (page)));
+  children = kgx_tab_get_children (KGX_TAB (adw_tab_page_get_child (page)));
 
   if (children->len < 1) {
     return FALSE; // Aka no, I don’t want to block closing
   }
 
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+  root = gtk_widget_get_root (GTK_WIDGET (self));
 
   dlg = kgx_close_dialog_new (KGX_CONTEXT_TAB, children);
 
-  gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel));
+  gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (root));
 
   g_signal_connect (dlg, "response", G_CALLBACK (close_response), page);
 
@@ -505,8 +504,8 @@ close_page (HdyTabView *view,
 
 
 static void
-setup_menu (HdyTabView *view,
-            HdyTabPage *page,
+setup_menu (AdwTabView *view,
+            AdwTabPage *page,
             KgxPages   *self)
 {
   KgxPagesPrivate *priv = kgx_pages_get_instance_private (self);
@@ -557,13 +556,13 @@ kgx_pages_class_init (KgxPagesClass *klass)
   /**
    * KgxPages:tab-view:
    *
-   * The #HdyTabView
+   * The #AdwTabView
    *
    * Stability: Private
    */
   pspecs[PROP_TAB_VIEW] =
     g_param_spec_object ("tab-view", "Tab View", "The tab view",
-                         HDY_TYPE_TAB_VIEW,
+                         ADW_TYPE_TAB_VIEW,
                          G_PARAM_READABLE);
 
   /**
@@ -720,6 +719,14 @@ kgx_pages_init (KgxPages *self)
   priv->opaque = FALSE;
 
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  adw_tab_view_remove_shortcuts (ADW_TAB_VIEW (priv->view),
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_HOME |
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_END |
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_SHIFT_PAGE_UP |
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_SHIFT_PAGE_DOWN |
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_SHIFT_HOME |
+                                 ADW_TAB_VIEW_SHORTCUT_CONTROL_SHIFT_END);
 }
 
 
@@ -728,7 +735,7 @@ kgx_pages_add_page (KgxPages *self,
                     KgxTab   *tab)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *page;
+  AdwTabPage *page;
 
   g_return_if_fail (KGX_IS_PAGES (self));
 
@@ -736,7 +743,7 @@ kgx_pages_add_page (KgxPages *self,
 
   kgx_tab_set_initial_title (tab, priv->title, priv->path);
 
-  page = hdy_tab_view_add_page (HDY_TAB_VIEW (priv->view), GTK_WIDGET (tab), NULL);
+  page = adw_tab_view_add_page (ADW_TAB_VIEW (priv->view), GTK_WIDGET (tab), NULL);
   g_object_bind_property (tab, "tab-title", page, "title", G_BINDING_SYNC_CREATE);
   g_object_bind_property (tab, "tab-tooltip", page, "tooltip", G_BINDING_SYNC_CREATE);
   g_object_bind_property (tab, "needs-attention", page, "needs-attention", G_BINDING_SYNC_CREATE);
@@ -750,7 +757,7 @@ kgx_pages_remove_page (KgxPages *self,
                        KgxTab   *page)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *tab_page;
+  AdwTabPage *tab_page;
 
   g_return_if_fail (KGX_IS_PAGES (self));
 
@@ -758,15 +765,15 @@ kgx_pages_remove_page (KgxPages *self,
 
   if (!page)
     {
-      tab_page = hdy_tab_view_get_selected_page (HDY_TAB_VIEW (priv->view));
-      hdy_tab_view_close_page (HDY_TAB_VIEW (priv->view), tab_page);
+      tab_page = adw_tab_view_get_selected_page (ADW_TAB_VIEW (priv->view));
+      adw_tab_view_close_page (ADW_TAB_VIEW (priv->view), tab_page);
       return;
     }
 
   g_return_if_fail (KGX_IS_TAB (page));
 
-  tab_page = hdy_tab_view_get_page (HDY_TAB_VIEW (priv->view), GTK_WIDGET (page));
-  hdy_tab_view_close_page (HDY_TAB_VIEW (priv->view), tab_page);
+  tab_page = adw_tab_view_get_page (ADW_TAB_VIEW (priv->view), GTK_WIDGET (page));
+  adw_tab_view_close_page (ADW_TAB_VIEW (priv->view), tab_page);
 }
 
 
@@ -782,19 +789,19 @@ kgx_pages_focus_page (KgxPages *self,
                       KgxTab  *page)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *index;
+  AdwTabPage *index;
 
   g_return_if_fail (KGX_IS_PAGES (self));
   g_return_if_fail (KGX_IS_TAB (page));
 
   priv = kgx_pages_get_instance_private (self);
 
-  index = hdy_tab_view_get_page (HDY_TAB_VIEW (priv->view),
+  index = adw_tab_view_get_page (ADW_TAB_VIEW (priv->view),
                                  GTK_WIDGET (page));
 
   g_return_if_fail (index != NULL);
 
-  hdy_tab_view_set_selected_page (HDY_TAB_VIEW (priv->view), index);
+  adw_tab_view_set_selected_page (ADW_TAB_VIEW (priv->view), index);
 
   gtk_widget_grab_focus (GTK_WIDGET (page));
 }
@@ -834,7 +841,7 @@ kgx_pages_count (KgxPages *self)
 
   priv = kgx_pages_get_instance_private (self);
 
-  return hdy_tab_view_get_n_pages (HDY_TAB_VIEW (priv->view));
+  return adw_tab_view_get_n_pages (ADW_TAB_VIEW (priv->view));
 }
 
 
@@ -860,13 +867,13 @@ kgx_pages_get_children (KgxPages *self)
 
   children = g_ptr_array_new_full (10, (GDestroyNotify) kgx_process_unref);
 
-  n = hdy_tab_view_get_n_pages (HDY_TAB_VIEW (priv->view));
+  n = adw_tab_view_get_n_pages (ADW_TAB_VIEW (priv->view));
 
   for (guint i = 0; i < n; i++) {
-    HdyTabPage *page = hdy_tab_view_get_nth_page (HDY_TAB_VIEW (priv->view), i);
+    AdwTabPage *page = adw_tab_view_get_nth_page (ADW_TAB_VIEW (priv->view), i);
     g_autoptr (GPtrArray) page_children = NULL;
 
-    page_children = kgx_tab_get_children (KGX_TAB (hdy_tab_page_get_child (page)));
+    page_children = kgx_tab_get_children (KGX_TAB (adw_tab_page_get_child (page)));
 
     for (int j = 0; j < page_children->len; j++) {
       g_ptr_array_add (children, g_ptr_array_steal_index (page_children, j));
@@ -879,44 +886,11 @@ kgx_pages_get_children (KgxPages *self)
 }
 
 
-void
-kgx_pages_set_shortcut_widget (KgxPages  *self,
-                               GtkWidget *widget)
-{
-  KgxPagesPrivate *priv;
-
-  g_return_if_fail (KGX_IS_PAGES (self));
-  g_return_if_fail (GTK_IS_WIDGET (widget) || widget == NULL);
-
-  priv = kgx_pages_get_instance_private (self);
-
-  hdy_tab_view_set_shortcut_widget (HDY_TAB_VIEW (priv->view), widget);
-}
-
-
-gboolean
-kgx_pages_key_press_event (KgxPages *self,
-                           GdkEvent *event)
-{
-  KgxPagesPrivate *priv;
-
-  g_return_val_if_fail (KGX_IS_PAGES (self), GDK_EVENT_PROPAGATE);
-  g_return_val_if_fail (event != NULL, GDK_EVENT_PROPAGATE);
-
-  priv = kgx_pages_get_instance_private (self);
-
-  if (!priv->active_page)
-    return GDK_EVENT_PROPAGATE;
-
-  return kgx_tab_key_press_event (priv->active_page, event);
-}
-
-
 void
 kgx_pages_close_page (KgxPages *self)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *page;
+  AdwTabPage *page;
 
   g_return_if_fail (KGX_IS_PAGES (self));
 
@@ -924,9 +898,9 @@ kgx_pages_close_page (KgxPages *self)
   page = priv->action_page;
 
   if (!page)
-    page = hdy_tab_view_get_selected_page (HDY_TAB_VIEW (priv->view));
+    page = adw_tab_view_get_selected_page (ADW_TAB_VIEW (priv->view));
 
-  hdy_tab_view_close_page (HDY_TAB_VIEW (priv->view), page);
+  adw_tab_view_close_page (ADW_TAB_VIEW (priv->view), page);
 }
 
 
@@ -934,8 +908,8 @@ void
 kgx_pages_detach_page (KgxPages *self)
 {
   KgxPagesPrivate *priv;
-  HdyTabPage *page;
-  HdyTabView *new_view;
+  AdwTabPage *page;
+  AdwTabView *new_view;
 
   g_return_if_fail (KGX_IS_PAGES (self));
 
@@ -943,8 +917,8 @@ kgx_pages_detach_page (KgxPages *self)
   page = priv->action_page;
 
   if (!page)
-    page = hdy_tab_view_get_selected_page (HDY_TAB_VIEW (priv->view));
+    page = adw_tab_view_get_selected_page (ADW_TAB_VIEW (priv->view));
 
-  new_view = create_window (HDY_TAB_VIEW (priv->view), self);
-  hdy_tab_view_transfer_page (HDY_TAB_VIEW (priv->view), page, new_view, 0);
+  new_view = create_window (ADW_TAB_VIEW (priv->view), self);
+  adw_tab_view_transfer_page (ADW_TAB_VIEW (priv->view), page, new_view, 0);
 }
diff --git a/src/kgx-pages.h b/src/kgx-pages.h
index 9a03c77..11b58b0 100644
--- a/src/kgx-pages.h
+++ b/src/kgx-pages.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include <gtk/gtk.h>
+#include <adwaita.h>
 
 #include "kgx-tab.h"
 
@@ -32,13 +33,13 @@ G_BEGIN_DECLS
 struct _KgxPagesClass
 {
   /*< private >*/
-  GtkBinClass parent;
+  AdwBinClass parent;
 
   /*< public >*/
 };
 
 
-G_DECLARE_DERIVABLE_TYPE (KgxPages, kgx_pages, KGX, PAGES, GtkBin)
+G_DECLARE_DERIVABLE_TYPE (KgxPages, kgx_pages, KGX, PAGES, AdwBin)
 
 
 void        kgx_pages_add_page       (KgxPages   *self,
@@ -50,10 +51,6 @@ GPtrArray  *kgx_pages_get_children   (KgxPages   *self);
 void        kgx_pages_focus_page     (KgxPages   *self,
                                       KgxTab     *page);
 KgxStatus   kgx_pages_current_status (KgxPages   *self);
-void        kgx_pages_set_shortcut_widget (KgxPages  *self,
-                                           GtkWidget *widget);
-gboolean    kgx_pages_key_press_event     (KgxPages  *self,
-                                           GdkEvent  *event);
 void        kgx_pages_close_page          (KgxPages  *self);
 void        kgx_pages_detach_page         (KgxPages  *self);
 
diff --git a/src/kgx-pages.ui b/src/kgx-pages.ui
index 2023914..591dcf2 100644
--- a/src/kgx-pages.ui
+++ b/src/kgx-pages.ui
@@ -1,14 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
-  <template class="KgxPages" parent="GtkBin">
-    <property name="visible">True</property>
-    <child>
+  <requires lib="gtk" version="4.0"/>
+  <template class="KgxPages" parent="AdwBin">
+    <property name="child">
       <object class="GtkOverlay">
-        <property name="visible">True</property>
         <child>
-          <object class="HdyTabView" id="view">
-            <property name="visible">True</property>
+          <object class="AdwTabView" id="view">
             <property name="menu-model">tab-menu</property>
             <property name="default-icon">view_default_icon</property>
             <signal name="notify::selected-page" handler="page_changed" swapped="no"/>
@@ -21,25 +18,23 @@
         </child>
         <child type="overlay">
           <object class="GtkRevealer" id="status_revealer">
-            <property name="visible">False</property>
             <property name="halign">end</property>
             <property name="valign">end</property>
             <property name="transition-type">crossfade</property>
             <signal name="notify::child-revealed" handler="check_revealer" swapped="no"/>
-            <child>
+            <property name="child">
               <object class="GtkLabel" id="status">
-                <property name="visible">True</property>
                 <property name="label">WxH</property>
                 <style>
                   <class name="floating-bar"/>
                   <class name="numeric"/>
                 </style>
               </object>
-            </child>
+            </property>
           </object>
         </child>
       </object>
-    </child>
+    </property>
   </template>
   <menu id="tab-menu">
     <section>
diff --git a/src/kgx-simple-tab.ui b/src/kgx-simple-tab.ui
index bd22edf..e5bc986 100644
--- a/src/kgx-simple-tab.ui
+++ b/src/kgx-simple-tab.ui
@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gtk" version="4.0"/>
   <template class="KgxSimpleTab" parent="KgxTab">
-    <property name="visible">1</property>
     <child type="content">
       <object class="GtkScrolledWindow">
-        <property name="visible">1</property>
         <property name="vexpand">1</property>
         <property name="propagate-natural-width">1</property>
         <property name="propagate-natural-height">1</property>
@@ -13,13 +11,13 @@
         <style>
           <class name="terminal"/>
         </style>
-        <child>
+        <property name="child">
           <object class="KgxTerminal" id="terminal">
-            <property name="visible">True</property>
+            <property name="vexpand">True</property>
             <property name="allow-hyperlink">True</property>
             <signal name="notify::path" handler="path_changed" swapped="no"/>
           </object>
-        </child>
+        </property>
       </object>
     </child>
   </template>
diff --git a/src/kgx-tab-button.c b/src/kgx-tab-button.c
index 6891253..232ffec 100644
--- a/src/kgx-tab-button.c
+++ b/src/kgx-tab-button.c
@@ -18,12 +18,12 @@
  */
 
 /**
- * SECTION:hdy-tab-button
- * @short_description: A button that displays the number of #HdyTabView pages
+ * SECTION:kgx-tab-button
+ * @short_description: A button that displays the number of #AdwTabView pages
  * @title: KgxTabButton
  *
  * The #KgxTabButton widget is a #GtkButton subclass that displays the number
- * of pages in a given #HdyTabView.
+ * of pages in a given #AdwTabView.
  *
  * It can be used to open a tab switcher view in a mobile UI.
  *
@@ -50,7 +50,7 @@ struct _KgxTabButton {
   GtkLabel *label;
   GtkImage *icon;
 
-  HdyTabView *view;
+  AdwTabView *view;
 };
 
 
@@ -107,7 +107,7 @@ update_icon (KgxTabButton *self)
   GtkStyleContext *context;
 
   if (self->view) {
-    guint n_pages = hdy_tab_view_get_n_pages (self->view);
+    guint n_pages = adw_tab_view_get_n_pages (self->view);
 
     small_label = n_pages >= 10;
 
@@ -129,7 +129,7 @@ update_icon (KgxTabButton *self)
 
   gtk_widget_set_visible (GTK_WIDGET (self->label), display_label);
   gtk_label_set_text (self->label, label_text);
-  gtk_image_set_from_icon_name (self->icon, icon_name, GTK_ICON_SIZE_BUTTON);
+  gtk_image_set_from_icon_name (self->icon, icon_name);
 }
 
 
@@ -193,13 +193,13 @@ kgx_tab_button_class_init (KgxTabButtonClass *klass)
   /**
    * KgxTabButton:view:
    *
-   * The #HdyTabView the tab button displays.
+   * The #AdwTabView the tab button displays.
    */
   pspecs[PROP_VIEW] =
     g_param_spec_object ("view",
                          "View",
                          "The view the tab button displays.",
-                         HDY_TYPE_TAB_VIEW,
+                         ADW_TYPE_TAB_VIEW,
                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
   g_object_class_install_properties (object_class, LAST_PROP, pspecs);
@@ -248,11 +248,11 @@ kgx_tab_button_new (void)
  * kgx_tab_button_get_view:
  * @self: a #KgxTabButton
  *
- * Gets the #HdyTabView @self displays.
+ * Gets the #AdwTabView @self displays.
  *
- * Returns: (transfer none) (nullable): the #HdyTabView @self displays
+ * Returns: (transfer none) (nullable): the #AdwTabView @self displays
  */
-HdyTabView *
+AdwTabView *
 kgx_tab_button_get_view (KgxTabButton *self)
 {
   g_return_val_if_fail (KGX_IS_TAB_BUTTON (self), NULL);
@@ -264,16 +264,16 @@ kgx_tab_button_get_view (KgxTabButton *self)
 /**
  * kgx_tab_button_set_view:
  * @self: a #KgxTabButton
- * @view: (nullable): a #HdyTabView
+ * @view: (nullable): a #AdwTabView
  *
- * Sets the #HdyTabView @self displays.
+ * Sets the #AdwTabView @self displays.
  */
 void
 kgx_tab_button_set_view (KgxTabButton *self,
-                         HdyTabView   *view)
+                         AdwTabView   *view)
 {
   g_return_if_fail (KGX_IS_TAB_BUTTON (self));
-  g_return_if_fail (view == NULL || HDY_IS_TAB_VIEW (view));
+  g_return_if_fail (view == NULL || ADW_IS_TAB_VIEW (view));
 
   if (self->view == view) {
     return;
diff --git a/src/kgx-tab-button.h b/src/kgx-tab-button.h
index 7d157e2..b2d34bf 100644
--- a/src/kgx-tab-button.h
+++ b/src/kgx-tab-button.h
@@ -18,7 +18,7 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
@@ -28,8 +28,8 @@ G_DECLARE_FINAL_TYPE (KgxTabButton, kgx_tab_button, KGX, TAB_BUTTON, GtkButton)
 
 GtkWidget  *kgx_tab_button_new      (void);
 
-HdyTabView *kgx_tab_button_get_view (KgxTabButton *self);
+AdwTabView *kgx_tab_button_get_view (KgxTabButton *self);
 void        kgx_tab_button_set_view (KgxTabButton *self,
-                                     HdyTabView   *view);
+                                     AdwTabView   *view);
 
 G_END_DECLS
diff --git a/src/kgx-tab-button.ui b/src/kgx-tab-button.ui
index 4bd298e..359c298 100644
--- a/src/kgx-tab-button.ui
+++ b/src/kgx-tab-button.ui
@@ -1,22 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.24"/>
+  <requires lib="gtk" version="4.0"/>
   <template class="KgxTabButton" parent="GtkButton">
     <property name="tooltip_text" translatable="yes">View Open Tabs</property>
     <style>
       <class name="tab-button"/>
+      <class name="image-button"/>
     </style>
-    <child>
+    <property name="child">
       <object class="GtkOverlay">
-        <property name="visible">True</property>
         <child>
-          <object class="GtkImage" id="icon">
-            <property name="visible">True</property>
-          </object>
+          <object class="GtkImage" id="icon"/>
         </child>
         <child type="overlay">
           <object class="GtkLabel" id="label">
-            <property name="visible">True</property>
             <property name="halign">center</property>
             <property name="justify">center</property>
             <property name="width-chars">2</property>
@@ -24,11 +21,8 @@
               <attribute name="font-features" value="tnum=1"/>
             </attributes>
           </object>
-          <packing>
-            <property name="pass_through">True</property>
-          </packing>
         </child>
       </object>
-    </child>
+    </property>
   </template>
 </interface>
diff --git a/src/kgx-tab-switcher-row.c b/src/kgx-tab-switcher-row.c
index 8ba6ba0..fc243a5 100644
--- a/src/kgx-tab-switcher-row.c
+++ b/src/kgx-tab-switcher-row.c
@@ -32,8 +32,8 @@ struct _KgxTabSwitcherRow {
   GtkImage *indicator_icon;
   GtkWidget *close_btn;
 
-  HdyTabPage *page;
-  HdyTabView *view;
+  AdwTabPage *page;
+  AdwTabView *view;
 };
 
 
@@ -68,22 +68,22 @@ static void
 update_pinned (KgxTabSwitcherRow *self)
 {
   set_style_class (GTK_WIDGET (self), "pinned",
-                   hdy_tab_page_get_pinned (self->page));
+                   adw_tab_page_get_pinned (self->page));
 }
 
 
 static void
 update_icon (KgxTabSwitcherRow *self)
 {
-  GIcon *gicon = hdy_tab_page_get_icon (self->page);
-  gboolean loading = hdy_tab_page_get_loading (self->page);
+  GIcon *gicon = adw_tab_page_get_icon (self->page);
+  gboolean loading = adw_tab_page_get_loading (self->page);
   const char *name = loading ? "spinner" : "icon";
 
   if (!gicon) {
-    gicon = hdy_tab_view_get_default_icon (self->view);
+    gicon = adw_tab_view_get_default_icon (self->view);
   }
 
-  gtk_image_set_from_gicon (self->icon, gicon, GTK_ICON_SIZE_BUTTON);
+  gtk_image_set_from_gicon (self->icon, gicon);
   gtk_stack_set_visible_child_name (self->icon_stack, name);
 }
 
@@ -91,7 +91,7 @@ update_icon (KgxTabSwitcherRow *self)
 static void
 update_spinner (KgxTabSwitcherRow *self)
 {
-  gboolean loading = self->page && hdy_tab_page_get_loading (self->page);
+  gboolean loading = self->page && adw_tab_page_get_loading (self->page);
   gboolean mapped = gtk_widget_get_mapped (GTK_WIDGET (self));
 
   /* Don't use CPU when not needed */
@@ -109,17 +109,17 @@ update_loading (KgxTabSwitcherRow *self)
   update_icon (self);
   update_spinner (self);
   set_style_class (GTK_WIDGET (self), "loading",
-                   hdy_tab_page_get_loading (self->page));
+                   adw_tab_page_get_loading (self->page));
 }
 
 
 static void
 update_indicator (KgxTabSwitcherRow *self)
 {
-  GIcon *indicator = hdy_tab_page_get_indicator_icon (self->page);
-  gboolean activatable = hdy_tab_page_get_indicator_activatable (self->page);
+  GIcon *indicator = adw_tab_page_get_indicator_icon (self->page);
+  gboolean activatable = adw_tab_page_get_indicator_activatable (self->page);
 
-  gtk_image_set_from_gicon (self->indicator_icon, indicator, GTK_ICON_SIZE_BUTTON);
+  gtk_image_set_from_gicon (self->indicator_icon, indicator);
   gtk_widget_set_visible (GTK_WIDGET (self->indicator_btn), indicator != NULL);
   gtk_widget_set_sensitive (GTK_WIDGET (self->indicator_btn), activatable);
 }
@@ -129,7 +129,7 @@ static void
 update_needs_attention (KgxTabSwitcherRow *self)
 {
   set_style_class (GTK_WIDGET (self), "needs-attention",
-                   hdy_tab_page_get_needs_attention (self->page));
+                   adw_tab_page_get_needs_attention (self->page));
 }
 
 
@@ -151,7 +151,18 @@ close_clicked_cb (KgxTabSwitcherRow *self)
     return;
   }
 
-  hdy_tab_view_close_page (self->view, self->page);
+  adw_tab_view_close_page (self->view, self->page);
+}
+
+
+static void
+destroy_cb (KgxTabSwitcherRow *self)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self));
+
+  g_assert (GTK_IS_LIST_BOX (parent));
+
+  gtk_list_box_remove (GTK_LIST_BOX (parent), GTK_WIDGET (self));
 }
 
 
@@ -280,14 +291,14 @@ kgx_tab_switcher_row_class_init (KgxTabSwitcherRowClass *klass)
     g_param_spec_object ("page",
                          "Page",
                          "The page the row displays.",
-                         HDY_TYPE_TAB_PAGE,
+                         ADW_TYPE_TAB_PAGE,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
 
   pspecs[PROP_VIEW] =
     g_param_spec_object ("view",
                          "View",
                          "The view containing the page.",
-                         HDY_TYPE_TAB_VIEW,
+                         ADW_TYPE_TAB_VIEW,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
 
   g_object_class_install_properties (object_class, LAST_PROP, pspecs);
@@ -317,11 +328,11 @@ kgx_tab_switcher_row_init (KgxTabSwitcherRow *self)
 
 
 GtkWidget *
-kgx_tab_switcher_row_new (HdyTabPage *page,
-                          HdyTabView *view)
+kgx_tab_switcher_row_new (AdwTabPage *page,
+                          AdwTabView *view)
 {
-  g_return_val_if_fail (HDY_IS_TAB_PAGE (page), NULL);
-  g_return_val_if_fail (HDY_IS_TAB_VIEW (view), NULL);
+  g_return_val_if_fail (ADW_IS_TAB_PAGE (page), NULL);
+  g_return_val_if_fail (ADW_IS_TAB_VIEW (view), NULL);
 
   return g_object_new (KGX_TYPE_TAB_SWITCHER_ROW,
                        "page", page,
@@ -330,7 +341,7 @@ kgx_tab_switcher_row_new (HdyTabPage *page,
 }
 
 
-HdyTabPage *
+AdwTabPage *
 kgx_tab_switcher_row_get_page (KgxTabSwitcherRow *self)
 {
   g_return_val_if_fail (KGX_IS_TAB_SWITCHER_ROW (self), NULL);
@@ -359,7 +370,7 @@ kgx_tab_switcher_row_animate_close (KgxTabSwitcherRow *self)
   self->page = NULL;
 
   g_signal_connect_swapped (self->revealer, "notify::child-revealed",
-                            G_CALLBACK (gtk_widget_destroy), self);
+                            G_CALLBACK (destroy_cb), self);
 
   gtk_revealer_set_reveal_child (self->revealer, FALSE);
 }
diff --git a/src/kgx-tab-switcher-row.h b/src/kgx-tab-switcher-row.h
index eb3e487..dee0238 100644
--- a/src/kgx-tab-switcher-row.h
+++ b/src/kgx-tab-switcher-row.h
@@ -18,7 +18,7 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
@@ -26,10 +26,10 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (KgxTabSwitcherRow, kgx_tab_switcher_row, KGX, TAB_SWITCHER_ROW, GtkListBoxRow)
 
-GtkWidget  *kgx_tab_switcher_row_new           (HdyTabPage        *page,
-                                                HdyTabView        *view);
+GtkWidget  *kgx_tab_switcher_row_new           (AdwTabPage        *page,
+                                                AdwTabView        *view);
 
-HdyTabPage *kgx_tab_switcher_row_get_page      (KgxTabSwitcherRow *self);
+AdwTabPage *kgx_tab_switcher_row_get_page      (KgxTabSwitcherRow *self);
 
 gboolean    kgx_tab_switcher_row_is_animating  (KgxTabSwitcherRow *self);
 
diff --git a/src/kgx-tab-switcher-row.ui b/src/kgx-tab-switcher-row.ui
index 47604bf..41c0668 100644
--- a/src/kgx-tab-switcher-row.ui
+++ b/src/kgx-tab-switcher-row.ui
@@ -1,46 +1,43 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.24"/>
+  <requires lib="gtk" version="4.0"/>
   <template class="KgxTabSwitcherRow" parent="GtkListBoxRow">
-    <child>
+    <property name="child">
       <object class="GtkRevealer" id="revealer">
-        <property name="visible">True</property>
         <property name="transition-type">slide-up</property>
-        <child>
+        <property name="child">
           <object class="GtkBox">
-            <property name="visible">True</property>
             <property name="hexpand">False</property>
             <style>
               <class name="content"/>
             </style>
             <child>
               <object class="GtkStack" id="icon_stack">
-                <property name="visible">True</property>
                 <property name="margin-start">4</property>
                 <property name="margin-end">4</property>
                 <property name="valign">center</property>
                 <child>
-                  <object class="GtkImage" id="icon">
-                    <property name="visible">True</property>
-                    <property name="pixel-size">16</property>
-                  </object>
-                  <packing>
+                  <object class="GtkStackPage">
                     <property name="name">icon</property>
-                  </packing>
+                    <property name="child">
+                      <object class="GtkImage" id="icon">
+                        <property name="pixel-size">16</property>
+                      </object>
+                    </property>
+                  </object>
                 </child>
                 <child>
-                  <object class="GtkSpinner" id="spinner">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
+                  <object class="GtkStackPage">
                     <property name="name">spinner</property>
-                  </packing>
+                    <property name="child">
+                      <object class="GtkSpinner" id="spinner"/>
+                    </property>
+                  </object>
                 </child>
               </object>
             </child>
             <child>
               <object class="GtkLabel" id="title">
-                <property name="visible">True</property>
                 <property name="margin-start">4</property>
                 <property name="hexpand">True</property>
                 <property name="valign">center</property>
@@ -50,43 +47,36 @@
             </child>
             <child>
               <object class="GtkButton" id="indicator_btn">
-                <property name="visible">False</property>
                 <property name="can-focus">False</property>
                 <property name="valign">center</property>
                 <signal name="clicked" handler="indicator_clicked_cb" swapped="true" />
                 <style>
                   <class name="flat"/>
+                  <class name="image-button"/>
                   <class name="indicator-btn"/>
                 </style>
-                <child>
+                <property name="child">
                   <object class="GtkImage" id="indicator_icon">
-                    <property name="visible">True</property>
                     <property name="pixel-size">16</property>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
             <child>
               <object class="GtkButton" id="close_btn">
-                <property name="visible">True</property>
                 <property name="can-focus">False</property>
                 <property name="valign">center</property>
+                <property name="icon-name">window-close-symbolic</property>
                 <signal name="clicked" handler="close_clicked_cb" swapped="true" />
                 <style>
                   <class name="flat"/>
                   <class name="close-btn"/>
                 </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">window-close-symbolic</property>
-                  </object>
-                </child>
               </object>
             </child>
           </object>
-        </child>
+        </property>
       </object>
-    </child>
+    </property>
   </template>
 </interface>
diff --git a/src/kgx-tab-switcher.c b/src/kgx-tab-switcher.c
index fa94758..e1b0031 100644
--- a/src/kgx-tab-switcher.c
+++ b/src/kgx-tab-switcher.c
@@ -18,12 +18,12 @@
 
 /**
  * SECTION:kgx-tab-switcher
- * @short_description: A mobile tab switcher for HdyTabView
+ * @short_description: A mobile tab switcher for AdwTabView
  * @title: KgxTabSwitcher
  *
- * The #KgxTabSwitcher widget is a mobile tab switcher for HdyTabView. It's
+ * The #KgxTabSwitcher widget is a mobile tab switcher for AdwTabView. It's
  * supposed to be used in conjunction with #KgxTabButton to open it, and a
- * #HdyTabBar to provide UI for larger screens.
+ * #AdwTabBar to provide UI for larger screens.
  */
 
 #include "kgx-config.h"
@@ -32,26 +32,26 @@
 
 
 struct _KgxTabSwitcher {
-  GtkBin parent_instance;
+  GtkWidget parent_instance;
 
-  HdyFlap *flap;
+  AdwFlap *flap;
   GtkListBox *list;
 
   GtkGesture *click_gesture;
   GtkGesture *long_press_gesture;
-  GtkMenu *context_menu;
-  GtkPopover *touch_menu;
+  GtkPopover *context_menu;
 
-  HdyTabView *view;
+  AdwTabView *view;
   gboolean narrow;
 };
 
 
-G_DEFINE_TYPE (KgxTabSwitcher, kgx_tab_switcher, GTK_TYPE_BIN)
+G_DEFINE_TYPE (KgxTabSwitcher, kgx_tab_switcher, GTK_TYPE_WIDGET)
 
 
 enum {
   PROP_0,
+  PROP_CHILD,
   PROP_VIEW,
   PROP_NARROW,
   LAST_PROP
@@ -75,9 +75,9 @@ reset_setup_menu_cb (KgxTabSwitcher *self)
 
 
 static void
-touch_menu_notify_visible_cb (KgxTabSwitcher *self)
+context_menu_notify_visible_cb (KgxTabSwitcher *self)
 {
-  if (!self->touch_menu || gtk_widget_get_visible (GTK_WIDGET (self->touch_menu))) {
+  if (!self->context_menu || gtk_widget_get_visible (GTK_WIDGET (self->context_menu))) {
     return;
   }
 
@@ -86,18 +86,14 @@ touch_menu_notify_visible_cb (KgxTabSwitcher *self)
 
 
 static void
-destroy_cb (KgxTabSwitcher *self)
-{
-  self->touch_menu = NULL;
-}
-
-
-static void
-do_touch_popup (KgxTabSwitcher    *self,
-                KgxTabSwitcherRow *row)
+do_popup (KgxTabSwitcher    *self,
+          KgxTabSwitcherRow *row,
+          double             x,
+          double             y)
 {
-  GMenuModel *model = hdy_tab_view_get_menu_model (self->view);
-  HdyTabPage *page = kgx_tab_switcher_row_get_page (row);
+  GMenuModel *model = adw_tab_view_get_menu_model (self->view);
+  AdwTabPage *page = kgx_tab_switcher_row_get_page (row);
+  GdkRectangle rect;
 
   if (!G_IS_MENU_MODEL (model)) {
     return;
@@ -105,97 +101,68 @@ do_touch_popup (KgxTabSwitcher    *self,
 
   g_signal_emit_by_name (self->view, "setup-menu", page);
 
-  if (!self->touch_menu) {
-    self->touch_menu = GTK_POPOVER (gtk_popover_new_from_model (GTK_WIDGET (row), model));
-    gtk_popover_set_constrain_to (self->touch_menu, GTK_POPOVER_CONSTRAINT_WINDOW);
-
-    g_signal_connect_object (self->touch_menu, "notify::visible",
-                             G_CALLBACK (touch_menu_notify_visible_cb), self,
-                             G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
-    g_signal_connect_object (self->touch_menu, "destroy",
-                             G_CALLBACK (destroy_cb), self,
+  if (!self->context_menu) {
+    self->context_menu = GTK_POPOVER (gtk_popover_menu_new_from_model (model));
+    gtk_popover_set_position (self->context_menu, GTK_POS_BOTTOM);
+    gtk_popover_set_has_arrow (self->context_menu, FALSE);
+    gtk_widget_set_parent (GTK_WIDGET (self->context_menu) , GTK_WIDGET (self));
+
+    if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+      gtk_widget_set_halign (GTK_WIDGET (self->context_menu), GTK_ALIGN_END);
+    } else {
+      gtk_widget_set_halign (GTK_WIDGET (self->context_menu), GTK_ALIGN_START);
+    }
+
+    g_signal_connect_object (self->context_menu, "notify::visible",
+                             G_CALLBACK (context_menu_notify_visible_cb), self,
                              G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-  } else {
-    gtk_popover_set_relative_to (self->touch_menu, GTK_WIDGET (row));
   }
 
-  gtk_popover_popup (self->touch_menu);
-}
-
+  if (x >= 0 && y >= 0) {
+    graphene_rect_t bounds;
 
-static void
-popup_menu_detach (KgxTabSwitcher *self,
-                   GtkMenu        *menu)
-{
-  self->context_menu = NULL;
-}
+    g_assert (gtk_widget_compute_bounds (GTK_WIDGET (self->list),
+                                         GTK_WIDGET (self), &bounds));
 
+    rect.x = bounds.origin.x + x;
+    rect.y = bounds.origin.y + y;
+  } else {
+    graphene_rect_t bounds;
 
-static void
-popup_menu_deactivate_cb (KgxTabSwitcher *self)
-{
-  g_idle_add (G_SOURCE_FUNC (reset_setup_menu_cb), self);
-}
-
+    g_assert (gtk_widget_compute_bounds (GTK_WIDGET (row),
+                                         GTK_WIDGET (self), &bounds));
 
-static void
-do_popup (KgxTabSwitcher    *self,
-          KgxTabSwitcherRow *row,
-          GdkEvent          *event)
-{
-  GMenuModel *model = hdy_tab_view_get_menu_model (self->view);
-  HdyTabPage *page = kgx_tab_switcher_row_get_page (row);
+    rect.x = bounds.origin.x;
+    rect.y = bounds.origin.y + bounds.size.height;
 
-  if (!G_IS_MENU_MODEL (model)) {
-    return;
+    if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+      rect.x += bounds.size.width;
+    }
   }
 
-  g_signal_emit_by_name (self->view, "setup-menu", page);
+  rect.width = 0;
+  rect.height = 0;
 
-  if (!self->context_menu) {
-    self->context_menu = GTK_MENU (gtk_menu_new_from_model (model));
-    gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self->context_menu)),
-                                 GTK_STYLE_CLASS_CONTEXT_MENU);
-
-    g_signal_connect_object (self->context_menu,
-                             "deactivate",
-                             G_CALLBACK (popup_menu_deactivate_cb),
-                             self,
-                             G_CONNECT_SWAPPED);
+  gtk_popover_set_pointing_to (self->context_menu, &rect);
 
-    gtk_menu_attach_to_widget (self->context_menu, GTK_WIDGET (self),
-                               (GtkMenuDetachFunc) popup_menu_detach);
-  }
-
-  if (event && gdk_event_triggers_context_menu (event)) {
-    gtk_menu_popup_at_pointer (self->context_menu, event);
-  } else {
-    gtk_menu_popup_at_widget (self->context_menu,
-                              GTK_WIDGET (row),
-                              GDK_GRAVITY_SOUTH_WEST,
-                              GDK_GRAVITY_NORTH_WEST,
-                              event);
-
-    gtk_menu_shell_select_first (GTK_MENU_SHELL (self->context_menu), FALSE);
-  }
+  gtk_popover_popup (self->context_menu);
 }
 
 
 static void
 reveal_flap_cb (KgxTabSwitcher *self)
 {
-  HdyTabPage *page;
+  AdwTabPage *page;
   GtkWidget *child;
 
   gtk_widget_set_sensitive (GTK_WIDGET (self->view),
-                            !hdy_flap_get_reveal_flap (self->flap));
+                            !adw_flap_get_reveal_flap (self->flap));
 
-  if (hdy_flap_get_reveal_flap (self->flap)) {
+  if (adw_flap_get_reveal_flap (self->flap)) {
     gtk_widget_grab_focus (GTK_WIDGET (self->list));
   } else {
-    page = hdy_tab_view_get_selected_page (self->view);
-    child = hdy_tab_page_get_child (page);
+    page = adw_tab_view_get_selected_page (self->view);
+    child = adw_tab_page_get_child (page);
 
     gtk_widget_grab_focus (GTK_WIDGET (child));
   }
@@ -222,7 +189,7 @@ static void
 row_selected_cb (KgxTabSwitcher    *self,
                  KgxTabSwitcherRow *row)
 {
-  HdyTabPage *page;
+  AdwTabPage *page;
 
   if (!row) {
     return;
@@ -235,7 +202,7 @@ row_selected_cb (KgxTabSwitcher    *self,
   }
 
   page = kgx_tab_switcher_row_get_page (row);
-  hdy_tab_view_set_selected_page (self->view, page);
+  adw_tab_view_set_selected_page (self->view, page);
 }
 
 
@@ -280,7 +247,7 @@ pages_changed_cb (KgxTabSwitcher *self,
   }
 
   for (i = 0; i < added; i++) {
-    g_autoptr (HdyTabPage) page = g_list_model_get_item (pages, position + i);
+    g_autoptr (AdwTabPage) page = g_list_model_get_item (pages, position + i);
     GtkWidget *row = kgx_tab_switcher_row_new (page, self->view);
 
     gtk_list_box_insert (self->list, row, position + i);
@@ -290,16 +257,16 @@ pages_changed_cb (KgxTabSwitcher *self,
 
 
 static void
-notify_selected_page_cb (KgxTabSwitcher *self)
+selection_changed_cb (KgxTabSwitcher *self)
 {
-  HdyTabPage *page = NULL;
+  AdwTabPage *page = NULL;
 
   if (self->view) {
-    page = hdy_tab_view_get_selected_page (self->view);
+    page = adw_tab_view_get_selected_page (self->view);
   }
 
   if (page) {
-    int index = hdy_tab_view_get_page_position (self->view, page);
+    int index = adw_tab_view_get_page_position (self->view, page);
     KgxTabSwitcherRow *row = find_nth_alive_row (self, index);
 
     gtk_list_box_select_row (self->list, GTK_LIST_BOX_ROW (row));
@@ -315,9 +282,10 @@ click_pressed_cb (KgxTabSwitcher *self,
                   double          x,
                   double          y)
 {
-  g_autoptr (GdkEvent) event = NULL;
+  GdkEventSequence *sequence;
+  GdkEvent *event;
   GtkListBoxRow *row;
-  HdyTabPage *page;
+  AdwTabPage *page;
   guint button;
 
   if (n_press > 1) {
@@ -334,20 +302,21 @@ click_pressed_cb (KgxTabSwitcher *self,
     return;
   }
 
-  event = gtk_get_current_event ();
+  sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (self->click_gesture));
+  event = gtk_gesture_get_last_event (self->click_gesture, sequence);
   button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (self->click_gesture));
   page = kgx_tab_switcher_row_get_page (KGX_TAB_SWITCHER_ROW (row));
 
   if (event && gdk_event_triggers_context_menu (event)) {
     gtk_gesture_set_state (self->click_gesture, GTK_EVENT_SEQUENCE_CLAIMED);
-    do_popup (self, KGX_TAB_SWITCHER_ROW (row), event);
+    do_popup (self, KGX_TAB_SWITCHER_ROW (row), x, y);
 
     return;
   }
 
   if (button == GDK_BUTTON_MIDDLE) {
     gtk_gesture_set_state (self->click_gesture, GTK_EVENT_SEQUENCE_CLAIMED);
-    hdy_tab_view_close_page (self->view, page);
+    adw_tab_view_close_page (self->view, page);
 
     return;
   }
@@ -369,12 +338,21 @@ long_press_cb (KgxTabSwitcher *self,
     return;
   }
 
-  do_touch_popup (self, KGX_TAB_SWITCHER_ROW (row));
+  do_popup (self, KGX_TAB_SWITCHER_ROW (row), -1, -1);
 
   gtk_gesture_set_state (self->long_press_gesture, GTK_EVENT_SEQUENCE_CLAIMED);
 }
 
 
+static void
+popup_menu_cb (KgxTabSwitcher *self)
+{
+  GtkListBoxRow *row = gtk_list_box_get_selected_row (self->list);
+
+  do_popup (self, KGX_TAB_SWITCHER_ROW (row), -1, -1);
+}
+
+
 static void
 set_narrow (KgxTabSwitcher *self,
             gboolean        narrow)
@@ -400,8 +378,10 @@ kgx_tab_switcher_dispose (GObject *object)
 
   kgx_tab_switcher_set_view (self, NULL);
 
-  g_clear_object (&self->click_gesture);
-  g_clear_object (&self->long_press_gesture);
+  g_clear_pointer ((GtkWidget **) &self->flap, gtk_widget_unparent);
+  g_clear_pointer ((GtkWidget **) &self->context_menu, gtk_widget_unparent);
+  self->click_gesture = NULL;
+  self->long_press_gesture = NULL;
 
   G_OBJECT_CLASS (kgx_tab_switcher_parent_class)->dispose (object);
 }
@@ -416,6 +396,9 @@ kgx_tab_switcher_get_property (GObject    *object,
   KgxTabSwitcher *self = KGX_TAB_SWITCHER (object);
 
   switch (prop_id) {
+    case PROP_CHILD:
+      g_value_set_object (value, kgx_tab_switcher_get_child (self));
+      break;
     case PROP_VIEW:
       g_value_set_object (value, kgx_tab_switcher_get_view (self));
       break;
@@ -437,6 +420,9 @@ kgx_tab_switcher_set_property (GObject      *object,
   KgxTabSwitcher *self = KGX_TAB_SWITCHER (object);
 
   switch (prop_id) {
+    case PROP_CHILD:
+      kgx_tab_switcher_set_child (self, g_value_get_object (value));
+      break;
     case PROP_VIEW:
       kgx_tab_switcher_set_view (self, g_value_get_object (value));
       break;
@@ -447,133 +433,91 @@ kgx_tab_switcher_set_property (GObject      *object,
 
 
 static void
-kgx_tab_switcher_destroy (GtkWidget *widget)
-{
-  gtk_container_forall (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_destroy, NULL);
-
-  GTK_WIDGET_CLASS (kgx_tab_switcher_parent_class)->destroy (widget);
-}
-
-
-static gboolean
-kgx_tab_switcher_popup_menu (GtkWidget *widget)
+kgx_tab_switcher_measure (GtkWidget      *widget,
+                          GtkOrientation  orientation,
+                          int             for_size,
+                          int            *min,
+                          int            *nat,
+                          int            *min_baseline,
+                          int            *nat_baseline)
 {
   KgxTabSwitcher *self = KGX_TAB_SWITCHER (widget);
-  GtkListBoxRow *row = gtk_list_box_get_selected_row (self->list);
 
-  if (row) {
-    do_popup (self, KGX_TAB_SWITCHER_ROW (row), NULL);
-
-    return GDK_EVENT_STOP;
-  }
-
-  return GDK_EVENT_PROPAGATE;
+  gtk_widget_measure (GTK_WIDGET (self->flap), orientation, for_size,
+                      min, nat, min_baseline, nat_baseline);
 }
 
 
 static void
-kgx_tab_switcher_size_allocate (GtkWidget     *widget,
-                                GtkAllocation *alloc)
+kgx_tab_switcher_size_allocate (GtkWidget *widget,
+                                int        width,
+                                int        height,
+                                int        baseline)
 {
   KgxTabSwitcher *self = KGX_TAB_SWITCHER (widget);
 
-  set_narrow (self, alloc->width < 400);
-
-  GTK_WIDGET_CLASS (kgx_tab_switcher_parent_class)->size_allocate (widget, alloc);
-}
-
-
-static void
-kgx_tab_switcher_add (GtkContainer *container,
-                      GtkWidget    *widget)
-{
-  KgxTabSwitcher *self = KGX_TAB_SWITCHER (container);
-
-  if (!self->flap) {
-    GTK_CONTAINER_CLASS (kgx_tab_switcher_parent_class)->add (container, widget);
-
-    return;
-  }
-
-  hdy_flap_set_content (self->flap, widget);
-}
+  set_narrow (self, width < 400);
 
-
-static void
-kgx_tab_switcher_remove (GtkContainer *container,
-                         GtkWidget    *widget)
-{
-  KgxTabSwitcher *self = KGX_TAB_SWITCHER (container);
-
-  if (widget == GTK_WIDGET (self->flap)) {
-    GTK_CONTAINER_CLASS (kgx_tab_switcher_parent_class)->remove (container, widget);
-
-    return;
+  if (self->context_menu) {
+    gtk_popover_present (self->context_menu);
   }
 
-  hdy_flap_set_content (self->flap, NULL);
+  gtk_widget_allocate (GTK_WIDGET (self->flap), width, height, baseline, NULL);
 }
 
 
 static void
-kgx_tab_switcher_forall (GtkContainer *container,
-                         gboolean      include_internals,
-                         GtkCallback   callback,
-                         gpointer      callback_data)
+kgx_tab_switcher_direction_changed (GtkWidget        *widget,
+                                    GtkTextDirection  previous_direction)
 {
-  KgxTabSwitcher *self = KGX_TAB_SWITCHER (container);
-  GtkWidget *content;
-
-  if (include_internals) {
-    GTK_CONTAINER_CLASS (kgx_tab_switcher_parent_class)->forall (container,
-                                                                 include_internals,
-                                                                 callback,
-                                                                 callback_data);
-
-    return;
-  }
+  KgxTabSwitcher *self = KGX_TAB_SWITCHER (widget);
 
-  if (!self->flap) {
+  if (gtk_widget_get_direction (widget) == previous_direction) {
     return;
   }
 
-  content = hdy_flap_get_content (self->flap);
-
-  if (content) {
-    callback (content, callback_data);
+  if (self->context_menu) {
+    if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+      gtk_widget_set_halign (GTK_WIDGET (self->context_menu), GTK_ALIGN_END);
+    } else {
+      gtk_widget_set_halign (GTK_WIDGET (self->context_menu), GTK_ALIGN_START);
+    }
   }
 }
 
 
+
 static void
 kgx_tab_switcher_class_init (KgxTabSwitcherClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
   object_class->dispose = kgx_tab_switcher_dispose;
   object_class->get_property = kgx_tab_switcher_get_property;
   object_class->set_property = kgx_tab_switcher_set_property;
 
-  widget_class->destroy = kgx_tab_switcher_destroy;
-  widget_class->popup_menu = kgx_tab_switcher_popup_menu;
+  widget_class->measure = kgx_tab_switcher_measure;
   widget_class->size_allocate = kgx_tab_switcher_size_allocate;
+  widget_class->direction_changed = kgx_tab_switcher_direction_changed;
 
-  container_class->add = kgx_tab_switcher_add;
-  container_class->remove = kgx_tab_switcher_remove;
-  container_class->forall = kgx_tab_switcher_forall;
+  pspecs[PROP_CHILD] =
+    g_param_spec_object ("child",
+                         "Child",
+                         "The tab switcher child",
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
   /**
    * KgxTabSwitcher:view:
    *
-   * The #HdyTabView the tab switcher controls;
+   * The #AdwTabView the tab switcher controls;
    */
   pspecs[PROP_VIEW] =
     g_param_spec_object ("view",
                          "View",
                          "The view the tab switcher controls.",
-                         HDY_TYPE_TAB_VIEW,
+                         ADW_TYPE_TAB_VIEW,
                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
   pspecs[PROP_NARROW] =
@@ -605,6 +549,11 @@ kgx_tab_switcher_class_init (KgxTabSwitcherClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, new_tab_cb);
   gtk_widget_class_bind_template_callback (widget_class, row_selected_cb);
   gtk_widget_class_bind_template_callback (widget_class, row_activated_cb);
+
+  gtk_widget_class_install_action (widget_class, "menu.popup", NULL, (GtkWidgetActionActivateFunc) 
popup_menu_cb);
+
+  gtk_widget_class_add_binding_action (widget_class, GDK_KEY_F10, GDK_SHIFT_MASK, "menu.popup", NULL);
+  gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Menu, 0, "menu.popup", NULL);
 }
 
 
@@ -615,13 +564,15 @@ kgx_tab_switcher_init (KgxTabSwitcher *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  self->click_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self->list));
+  self->click_gesture = gtk_gesture_click_new ();
   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->click_gesture), 0);
   g_signal_connect_swapped (self->click_gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
+  gtk_widget_add_controller (GTK_WIDGET (self->list), GTK_EVENT_CONTROLLER (self->click_gesture));
 
-  self->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (self->list));
+  self->long_press_gesture = gtk_gesture_long_press_new ();
   gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (self->long_press_gesture), TRUE);
   g_signal_connect_swapped (self->long_press_gesture, "pressed", G_CALLBACK (long_press_cb), self);
+  gtk_widget_add_controller (GTK_WIDGET (self->list), GTK_EVENT_CONTROLLER (self->long_press_gesture));
 }
 
 
@@ -639,15 +590,41 @@ kgx_tab_switcher_new (void)
 }
 
 
+GtkWidget *
+kgx_tab_switcher_get_child (KgxTabSwitcher *self)
+{
+  g_return_val_if_fail (KGX_IS_TAB_SWITCHER (self), NULL);
+
+  return adw_flap_get_content (self->flap);
+}
+
+
+void
+kgx_tab_switcher_set_child (KgxTabSwitcher *self,
+                            GtkWidget      *child)
+{
+  g_return_if_fail (KGX_IS_TAB_SWITCHER (self));
+  g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
+
+  if (child == kgx_tab_switcher_get_child (self)) {
+    return;
+  }
+
+  adw_flap_set_content (self->flap, child);
+
+  g_object_notify_by_pspec (G_OBJECT (self), pspecs[PROP_CHILD]);
+}
+
+
 /**
  * kgx_tab_switcher_get_view:
  * @self: a #KgxTabSwitcher
  *
- * Gets the #HdyTabView @self controls.
+ * Gets the #AdwTabView @self controls.
  *
- * Returns: (transfer none) (nullable): the #HdyTabView @self controls
+ * Returns: (transfer none) (nullable): the #AdwTabView @self controls
  */
-HdyTabView *
+AdwTabView *
 kgx_tab_switcher_get_view (KgxTabSwitcher *self)
 {
   g_return_val_if_fail (KGX_IS_TAB_SWITCHER (self), NULL);
@@ -659,43 +636,43 @@ kgx_tab_switcher_get_view (KgxTabSwitcher *self)
 /**
  * kgx_tab_switcher_set_view:
  * @self: a #KgxTabSwitcher
- * @view: (nullable): a #HdyTabView
+ * @view: (nullable): a #AdwTabView
  *
- * Sets the #HdyTabView @self controls.
+ * Sets the #AdwTabView @self controls.
  */
 void
 kgx_tab_switcher_set_view (KgxTabSwitcher *self,
-                           HdyTabView     *view)
+                           AdwTabView     *view)
 {
   g_return_if_fail (KGX_IS_TAB_SWITCHER (self));
-  g_return_if_fail (view == NULL || HDY_IS_TAB_VIEW (view));
+  g_return_if_fail (view == NULL || ADW_IS_TAB_VIEW (view));
 
   if (self->view == view) {
     return;
   }
 
   if (self->view) {
-    GListModel *pages = hdy_tab_view_get_pages (self->view);
+    GtkSelectionModel *pages = adw_tab_view_get_pages (self->view);
 
-    g_signal_handlers_disconnect_by_func (self->view, G_CALLBACK (notify_selected_page_cb), self);
+    g_signal_handlers_disconnect_by_func (pages, G_CALLBACK (selection_changed_cb), self);
     g_signal_handlers_disconnect_by_func (pages, G_CALLBACK (pages_changed_cb), self);
   }
 
   g_set_object (&self->view, view);
 
   if (self->view) {
-    GListModel *pages = hdy_tab_view_get_pages (self->view);
+    GtkSelectionModel *pages = adw_tab_view_get_pages (self->view);
 
     g_signal_connect_object (pages, "items-changed",
                              G_CALLBACK (pages_changed_cb), self,
                              G_CONNECT_SWAPPED);
 
-    g_signal_connect_object (self->view, "notify::selected-page",
-                             G_CALLBACK (notify_selected_page_cb), self,
+    g_signal_connect_object (pages, "selection-changed",
+                             G_CALLBACK (selection_changed_cb), self,
                              G_CONNECT_SWAPPED);
   }
 
-  notify_selected_page_cb (self);
+  selection_changed_cb (self);
 
   g_object_notify_by_pspec (G_OBJECT (self), pspecs[PROP_VIEW]);
 }
@@ -706,7 +683,7 @@ kgx_tab_switcher_open (KgxTabSwitcher *self)
 {
   g_return_if_fail (KGX_IS_TAB_SWITCHER (self));
 
-  hdy_flap_set_reveal_flap (self->flap, TRUE);
+  adw_flap_set_reveal_flap (self->flap, TRUE);
 }
 
 
@@ -715,7 +692,7 @@ kgx_tab_switcher_close (KgxTabSwitcher *self)
 {
   g_return_if_fail (KGX_IS_TAB_SWITCHER (self));
 
-  hdy_flap_set_reveal_flap (self->flap, FALSE);
+  adw_flap_set_reveal_flap (self->flap, FALSE);
 }
 
 
diff --git a/src/kgx-tab-switcher.h b/src/kgx-tab-switcher.h
index b120f63..8473f53 100644
--- a/src/kgx-tab-switcher.h
+++ b/src/kgx-tab-switcher.h
@@ -18,19 +18,23 @@
 
 #pragma once
 
-#include <handy.h>
+#include <adwaita.h>
 
 G_BEGIN_DECLS
 
 #define KGX_TYPE_TAB_SWITCHER (kgx_tab_switcher_get_type())
 
-G_DECLARE_FINAL_TYPE (KgxTabSwitcher, kgx_tab_switcher, KGX, TAB_SWITCHER, GtkBin)
+G_DECLARE_FINAL_TYPE (KgxTabSwitcher, kgx_tab_switcher, KGX, TAB_SWITCHER, GtkWidget)
 
 GtkWidget   *kgx_tab_switcher_new       (void);
 
-HdyTabView *kgx_tab_switcher_get_view   (KgxTabSwitcher *self);
+GtkWidget *kgx_tab_switcher_get_child   (KgxTabSwitcher *self);
+void       kgx_tab_switcher_set_child   (KgxTabSwitcher *self,
+                                         GtkWidget      *child);
+
+AdwTabView *kgx_tab_switcher_get_view   (KgxTabSwitcher *self);
 void        kgx_tab_switcher_set_view   (KgxTabSwitcher *self,
-                                         HdyTabView     *view);
+                                         AdwTabView     *view);
 
 void        kgx_tab_switcher_open       (KgxTabSwitcher *self);
 void        kgx_tab_switcher_close      (KgxTabSwitcher *self);
diff --git a/src/kgx-tab-switcher.ui b/src/kgx-tab-switcher.ui
index faa3b4b..7e22615 100644
--- a/src/kgx-tab-switcher.ui
+++ b/src/kgx-tab-switcher.ui
@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.24"/>
-  <template class="KgxTabSwitcher" parent="GtkBin">
+  <requires lib="gtk" version="4.0"/>
+  <template class="KgxTabSwitcher" parent="GtkWidget">
     <child>
-      <object class="HdyFlap" id="flap">
-        <property name="visible">True</property>
+      <object class="AdwFlap" id="flap">
         <property name="orientation">vertical</property>
         <property name="swipe-to-open">False</property>
         <property name="swipe-to-close">False</property>
@@ -12,70 +11,64 @@
         <property name="flap-position">end</property>
         <property name="fold-policy">always</property>
         <signal name="notify::reveal-flap" handler="reveal_flap_cb" swapped="true"/>
-        <child type="flap">
+        <property name="flap">
           <object class="GtkOverlay">
-            <property name="visible">True</property>
             <style>
               <class name="tab-switcher"/>
             </style>
-            <child>
+            <property name="child">
               <object class="GtkScrolledWindow">
-                <property name="visible">True</property>
                 <property name="hscrollbar-policy">never</property>
                 <property name="propagate-natural-width">True</property>
                 <property name="propagate-natural-height">True</property>
                 <style>
                   <class name="view"/>
                 </style>
-                <child>
+                <property name="child">
                   <object class="GtkListBox" id="list">
-                    <property name="visible">True</property>
                     <property name="selection-mode">single</property>
                     <signal name="row-selected" handler="row_selected_cb" swapped="true"/>
                     <signal name="row-activated" handler="row_activated_cb" swapped="true"/>
+                    <style>
+                      <class name="navigation-sidebar"/>
+                    </style>
                   </object>
-                </child>
+                </property>
               </object>
-            </child>
+            </property>
             <child type="overlay">
               <object class="GtkButton">
-                <property name="visible">True</property>
                 <property name="halign">center</property>
                 <property name="valign">start</property>
                 <property name="can-focus">False</property>
                 <signal name="clicked" handler="collapse_cb" swapped="true" />
                 <style>
+                  <class name="flat"/>
                   <class name="collapse-button"/>
+                  <class name="image-button"/>
                 </style>
-                <child>
+                <property name="child">
                   <object class="GtkImage">
-                    <property name="visible">True</property>
                     <property name="pixel-size">24</property>
                     <property name="icon-name">tab-switcher-collapse-symbolic</property>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
             <child type="overlay">
               <object class="GtkButton">
-                <property name="visible">True</property>
                 <property name="halign">end</property>
                 <property name="valign">end</property>
+                <property name="icon-name">list-add-symbolic</property>
                 <signal name="clicked" handler="new_tab_cb" swapped="true" />
                 <style>
                   <class name="suggested-action"/>
                   <class name="new-tab-button"/>
                 </style>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">list-add-symbolic</property>
-                  </object>
-                </child>
               </object>
             </child>
           </object>
-        </child>
+        </property>
       </object>
     </child>
   </template>
diff --git a/src/kgx-tab.c b/src/kgx-tab.c
index 706400c..16d0ec5 100644
--- a/src/kgx-tab.c
+++ b/src/kgx-tab.c
@@ -201,14 +201,14 @@ search_enabled (GObject    *object,
 {
   KgxTabPrivate *priv = kgx_tab_get_instance_private (self);
 
-  if (!hdy_search_bar_get_search_mode (HDY_SEARCH_BAR (priv->search_bar))) {
+  if (!gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (priv->search_bar))) {
     gtk_widget_grab_focus (GTK_WIDGET (self));
   }
 }
 
 
 static void
-search_changed (HdySearchBar *bar,
+search_changed (GtkSearchBar *bar,
                 KgxTab       *self)
 {
   KgxTabPrivate *priv = kgx_tab_get_instance_private (self);
@@ -218,7 +218,7 @@ search_changed (HdySearchBar *bar,
   gboolean narrowing_down;
   guint32 flags = PCRE2_MULTILINE;
 
-  search = gtk_entry_get_text (GTK_ENTRY (priv->search_entry));
+  search = gtk_editable_get_text (GTK_EDITABLE (priv->search_entry));
 
   if (search) {
     g_autofree char *lowercase = g_utf8_strdown (search, -1);
@@ -271,7 +271,7 @@ search_changed (HdySearchBar *bar,
 
 
 static void
-search_next (HdySearchBar *bar,
+search_next (GtkSearchBar *bar,
              KgxTab       *self)
 {
   KgxTabPrivate *priv = kgx_tab_get_instance_private (self);
@@ -281,7 +281,7 @@ search_next (HdySearchBar *bar,
 
 
 static void
-search_prev (HdySearchBar *bar,
+search_prev (GtkSearchBar *bar,
              KgxTab       *self)
 {
   KgxTabPrivate *priv = kgx_tab_get_instance_private (self);
@@ -474,48 +474,17 @@ kgx_tab_set_property (GObject      *object,
 
 
 static gboolean
-kgx_tab_draw (GtkWidget *widget,
-              cairo_t   *cr)
+drop (GtkDropTarget *target,
+      const GValue  *value,
+      KgxTab        *self)
 {
-  GtkStateFlags flags;
+  kgx_tab_accept_drop (self, value);
 
-  GTK_WIDGET_CLASS (kgx_tab_parent_class)->draw (widget, cr);
-
-  flags = gtk_widget_get_state_flags (widget);
-
-  /* FIXME this is a workaround for the fact GTK3 css outline is used for focus
-   * only by default. This can be removed in GTK4 as it just works there. */
-  if (flags & GTK_STATE_FLAG_DROP_ACTIVE) {
-    GtkStyleContext *context = gtk_widget_get_style_context (widget);
-    int width = gtk_widget_get_allocated_width (widget);
-    int height = gtk_widget_get_allocated_height (widget);
-
-    gtk_render_focus (context, cr, 0, 0, width, height);
-  }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-
-static void
-kgx_tab_drag_data_received (GtkWidget        *widget,
-                            GdkDragContext   *context,
-                            gint              x,
-                            gint              y,
-                            GtkSelectionData *selection_data,
-                            guint             info,
-                            guint             time)
-{
-  KgxTab *self = KGX_TAB (widget);
-  GdkDragAction action = gdk_drag_context_get_selected_action (context);
-
-  kgx_tab_accept_drop (self, selection_data);
-
-  gtk_drag_finish (context, TRUE, action == GDK_ACTION_COPY, time);
+  return TRUE;
 }
 
 
-static void
+static gboolean
 kgx_tab_grab_focus (GtkWidget *widget)
 {
   KgxTab *self = KGX_TAB (widget);
@@ -523,10 +492,10 @@ kgx_tab_grab_focus (GtkWidget *widget)
 
   if (priv->terminal) {
     gtk_widget_grab_focus (GTK_WIDGET (priv->terminal));
-    return;
+    return GDK_EVENT_STOP;
   }
 
-  GTK_WIDGET_CLASS (kgx_tab_parent_class)->grab_focus (widget);
+  return GTK_WIDGET_CLASS (kgx_tab_parent_class)->grab_focus (widget);
 }
 
 
@@ -561,8 +530,6 @@ kgx_tab_class_init (KgxTabClass *klass)
   object_class->get_property = kgx_tab_get_property;
   object_class->set_property = kgx_tab_set_property;
 
-  widget_class->draw = kgx_tab_draw;
-  widget_class->drag_data_received = kgx_tab_drag_data_received;
   widget_class->grab_focus = kgx_tab_grab_focus;
 
   tab_class->start = kgx_tab_real_start;
@@ -769,8 +736,8 @@ kgx_tab_add_child (GtkBuildable *buildable,
   if (type && g_str_equal (type, "content")) {
     g_set_weak_pointer (&priv->content, GTK_WIDGET (child));
     gtk_stack_add_named (GTK_STACK (priv->stack), GTK_WIDGET (child), "content");
-  } else {
-    gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (child));
+  } else if (GTK_IS_WIDGET (child)) {
+    gtk_box_append (GTK_BOX (self), GTK_WIDGET (child));
   }
 }
 
@@ -789,7 +756,6 @@ died (KgxTab         *self,
       gboolean        success)
 {
   KgxTabPrivate *priv;
-  GtkStyleContext *context;
 
   g_return_if_fail (KGX_IS_TAB (self));
 
@@ -797,12 +763,10 @@ died (KgxTab         *self,
 
   gtk_label_set_markup (GTK_LABEL (priv->label), message);
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (priv->revealer));
-
   if (type == GTK_MESSAGE_ERROR) {
-    gtk_style_context_add_class (context, "error");
+    gtk_widget_add_css_class (priv->revealer, "error");
   } else {
-    gtk_style_context_remove_class (context, "error");
+    gtk_widget_remove_css_class (priv->revealer, "error");
   }
 
   gtk_revealer_set_reveal_child (GTK_REVEALER (priv->revealer), TRUE);
@@ -818,6 +782,7 @@ kgx_tab_init (KgxTab *self)
 {
   static guint last_id = 0;
   KgxTabPrivate *priv = kgx_tab_get_instance_private (self);
+  GtkDropTarget *target;
 
   last_id++;
 
@@ -834,13 +799,12 @@ kgx_tab_init (KgxTab *self)
 
   g_signal_connect (self, "died", G_CALLBACK (died), NULL);
 
-  hdy_search_bar_connect_entry (HDY_SEARCH_BAR (priv->search_bar),
-                                GTK_ENTRY (priv->search_entry));
+  gtk_search_bar_connect_entry (GTK_SEARCH_BAR (priv->search_bar),
+                                GTK_EDITABLE (priv->search_entry));
 
-  gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, NULL, 0,
-                     GDK_ACTION_COPY);
-  gtk_drag_dest_add_uri_targets (GTK_WIDGET (self));
-  gtk_drag_dest_add_text_targets (GTK_WIDGET (self));
+  target = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
+  g_signal_connect (target, "drop", G_CALLBACK (drop), self);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (target));
 }
 
 
@@ -907,45 +871,6 @@ kgx_tab_connect_terminal (KgxTab      *self,
 }
 
 
-void
-kgx_tab_set_pages (KgxTab   *self,
-                   KgxPages *pages)
-{
-  KgxTabPrivate *priv;
-
-  g_return_if_fail (KGX_IS_TAB (self));
-  g_return_if_fail (KGX_IS_PAGES (pages) || !pages);
-
-  priv = kgx_tab_get_instance_private (self);
-
-  g_clear_object (&priv->pages_font_bind);
-  g_clear_object (&priv->pages_zoom_bind);
-  g_clear_object (&priv->pages_theme_bind);
-  g_clear_object (&priv->pages_opaque_bind);
-  g_clear_object (&priv->pages_scrollback_bind);
-
-  if (pages == NULL) {
-    return;
-  }
-
-  priv->pages_font_bind = g_object_bind_property (pages, "font",
-                                                  self, "font",
-                                                  G_BINDING_SYNC_CREATE);
-  priv->pages_zoom_bind = g_object_bind_property (pages, "zoom",
-                                                  self, "zoom",
-                                                  G_BINDING_SYNC_CREATE);
-  priv->pages_theme_bind = g_object_bind_property (pages, "theme",
-                                                   self, "theme",
-                                                   G_BINDING_SYNC_CREATE);
-  priv->pages_opaque_bind = g_object_bind_property (pages, "opaque",
-                                                    self, "opaque",
-                                                    G_BINDING_SYNC_CREATE);
-  priv->pages_scrollback_bind = g_object_bind_property (pages, "scrollback-lines",
-                                                        self, "scrollback-lines",
-                                                        G_BINDING_SYNC_CREATE);
-}
-
-
 void
 kgx_tab_start (KgxTab              *self,
                GAsyncReadyCallback  callback,
@@ -1226,33 +1151,22 @@ kgx_tab_get_children (KgxTab *self)
 
 
 void
-kgx_tab_accept_drop (KgxTab           *self,
-                     GtkSelectionData *selection_data)
+kgx_tab_accept_drop (KgxTab       *self,
+                     const GValue *value)
 {
   KgxTabPrivate *priv;
-  GdkAtom selection_data_target;
   g_autofree char *text = NULL;
+  g_auto (GStrv) uris = NULL;
 
   g_return_if_fail (KGX_IS_TAB (self));
 
   priv = kgx_tab_get_instance_private (self);
 
-  selection_data_target = gtk_selection_data_get_target (selection_data);
-
-  if (gtk_selection_data_get_length (selection_data) < 0)
-    return;
-
-  if (gtk_targets_include_uri (&selection_data_target, 1)) {
-    g_auto (GStrv) uris = NULL;
-
-    uris = gtk_selection_data_get_uris (selection_data);
+  uris = g_strsplit (g_value_get_string (value), "\n", 0);
 
-    kgx_util_transform_uris_to_quoted_fuse_paths (uris);
+  kgx_util_transform_uris_to_quoted_fuse_paths (uris);
 
-    text = kgx_util_concat_uris (uris, NULL);
-  } else {
-    text = (char *) gtk_selection_data_get_text (selection_data);
-  }
+  text = kgx_util_concat_uris (uris, NULL);
 
   if (priv->terminal) {
     kgx_terminal_accept_paste (KGX_TERMINAL (priv->terminal), text);
@@ -1281,26 +1195,40 @@ kgx_tab_set_initial_title (KgxTab     *self,
 }
 
 
-gboolean
-kgx_tab_key_press_event (KgxTab   *self,
-                         GdkEvent *event)
+void
+kgx_tab_set_pages (KgxTab   *self,
+                   KgxPages *pages)
 {
   KgxTabPrivate *priv;
-  GtkWidget *toplevel, *focus;
 
-  g_return_val_if_fail (KGX_IS_TAB (self), GDK_EVENT_PROPAGATE);
-  g_return_val_if_fail (event != NULL, GDK_EVENT_PROPAGATE);
+  g_return_if_fail (KGX_IS_TAB (self));
+  g_return_if_fail (KGX_IS_PAGES (pages) || !pages);
 
   priv = kgx_tab_get_instance_private (self);
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
-
-  if (!GTK_IS_WINDOW (toplevel))
-    return GDK_EVENT_PROPAGATE;
 
-  focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
+  g_clear_object (&priv->pages_font_bind);
+  g_clear_object (&priv->pages_zoom_bind);
+  g_clear_object (&priv->pages_theme_bind);
+  g_clear_object (&priv->pages_opaque_bind);
+  g_clear_object (&priv->pages_scrollback_bind);
 
-  if (focus == GTK_WIDGET (priv->terminal))
-    return gtk_widget_event (GTK_WIDGET (priv->terminal), event);
+  if (pages == NULL) {
+    return;
+  }
 
-  return GDK_EVENT_PROPAGATE;
+  priv->pages_font_bind = g_object_bind_property (pages, "font",
+                                                  self, "font",
+                                                  G_BINDING_SYNC_CREATE);
+  priv->pages_zoom_bind = g_object_bind_property (pages, "zoom",
+                                                  self, "zoom",
+                                                  G_BINDING_SYNC_CREATE);
+  priv->pages_theme_bind = g_object_bind_property (pages, "theme",
+                                                   self, "theme",
+                                                   G_BINDING_SYNC_CREATE);
+  priv->pages_opaque_bind = g_object_bind_property (pages, "opaque",
+                                                    self, "opaque",
+                                                    G_BINDING_SYNC_CREATE);
+  priv->pages_scrollback_bind = g_object_bind_property (pages, "scrollback-lines",
+                                                        self, "scrollback-lines",
+                                                        G_BINDING_SYNC_CREATE);
 }
diff --git a/src/kgx-tab.h b/src/kgx-tab.h
index fdf0eae..77f8604 100644
--- a/src/kgx-tab.h
+++ b/src/kgx-tab.h
@@ -100,11 +100,9 @@ void        kgx_tab_pop_child        (KgxTab               *self,
 gboolean    kgx_tab_is_active        (KgxTab               *self);
 GPtrArray  *kgx_tab_get_children     (KgxTab               *self);
 void        kgx_tab_accept_drop      (KgxTab               *self,
-                                      GtkSelectionData     *selection_data);
+                                      const GValue         *value);
 void        kgx_tab_set_initial_title (KgxTab              *self,
                                        const char          *title,
                                        GFile               *path);
-gboolean    kgx_tab_key_press_event   (KgxTab              *self,
-                                       GdkEvent            *event);
 
 G_END_DECLS
diff --git a/src/kgx-tab.ui b/src/kgx-tab.ui
index 2888104..9f82414 100644
--- a/src/kgx-tab.ui
+++ b/src/kgx-tab.ui
@@ -1,27 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gtk" version="4.0"/>
   <template class="KgxTab" parent="GtkBox">
-    <property name="visible">True</property>
     <property name="orientation">vertical</property>
     <style>
       <class name="tab"/>
     </style>
     <child>
-      <object class="HdySearchBar" id="search_bar">
-        <property name="visible">1</property>
+      <object class="GtkSearchBar" id="search_bar">
         <property name="search-mode-enabled" bind-source="KgxTab" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
         <signal name="notify::search-mode-enabled" handler="search_enabled" swapped="no"/>
         <child>
-          <object class="HdyClamp">
-            <property name="visible">1</property>
+          <object class="AdwClamp">
+            <property name="hexpand">1</property>
             <property name="maximum-size">500</property>
-            <child>
+            <property name="child">
               <object class="GtkBox">
-                <property name="visible">1</property>
+                <property name="spacing">6</property>
                 <child>
                   <object class="GtkSearchEntry" id="search_entry">
-                    <property name="visible">1</property>
                     <property name="hexpand">1</property>
                     <signal name="next-match" handler="search_next" swapped="no"/>
                     <signal name="previous-match" handler="search_prev" swapped="no"/>
@@ -30,35 +27,20 @@
                 </child>
                 <child>
                   <object class="GtkButton">
-                    <property name="visible">1</property>
                     <property name="receives-default">1</property>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">1</property>
-                        <property name="icon-name">go-down-symbolic</property>
-                      </object>
-                    </child>
+                    <property name="icon-name">go-down-symbolic</property>
                     <signal name="clicked" handler="search_next" swapped="no"/>
                   </object>
                 </child>
                 <child>
                   <object class="GtkButton">
-                    <property name="visible">1</property>
                     <property name="receives-default">1</property>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">1</property>
-                        <property name="icon-name">go-up-symbolic</property>
-                      </object>
-                    </child>
+                    <property name="icon-name">go-up-symbolic</property>
                     <signal name="clicked" handler="search_prev" swapped="no"/>
                   </object>
                 </child>
-                <style>
-                  <class name="linked"/>
-                </style>
               </object>
-            </child>
+            </property>
           </object>
         </child>
         <style>
@@ -68,27 +50,23 @@
     </child>
     <child>
       <object class="GtkStack" id="stack">
-        <property name="vexpand">True</property>
         <property name="visible">True</property>
         <child>
           <object class="GtkBox">
-            <property name="visible">True</property>
             <style>
               <class name="empty-state"/>
             </style>
             <child>
               <object class="GtkRevealer" id="spinner_revealer">
-                <property name="visible">True</property>
                 <property name="hexpand">True</property>
                 <property name="transition-type">crossfade</property>
                 <property name="transition-duration">1000</property>
-                <child>
+                <property name="child">
                   <object class="GtkSpinner">
-                    <property name="visible">True</property>
                     <signal name="map" handler="spinner_mapped" swapped="no"/>
                     <signal name="unmap" handler="spinner_unmapped" swapped="no"/>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
           </object>
@@ -97,21 +75,16 @@
     </child>
     <child>
       <object class="GtkRevealer" id="revealer">
-        <property name="visible">True</property>
-        <property name="valign">end</property>
+        <property name="can_focus">False</property>
         <property name="transition_type">slide-up</property>
-        <child>
+        <property name="child">
           <object class="GtkBox">
-            <property name="visible">True</property>
             <property name="orientation">vertical</property>
             <child>
-              <object class="GtkSeparator">
-                <property name="visible">True</property>
-              </object>
+              <object class="GtkSeparator"/>
             </child>
             <child>
               <object class="GtkLabel" id="label">
-                <property name="visible">True</property>
                 <property name="use_markup">True</property>
                 <property name="wrap">True</property>
                 <property name="xalign">0</property>
@@ -121,11 +94,8 @@
               <class name="exit-info"/>
             </style>
           </object>
-        </child>
+        </property>
       </object>
-      <packing>
-        <property name="pack_type">end</property>
-      </packing>
     </child>
   </template>
 </interface>
diff --git a/src/kgx-terminal.c b/src/kgx-terminal.c
index d51020b..d307c9b 100644
--- a/src/kgx-terminal.c
+++ b/src/kgx-terminal.c
@@ -26,7 +26,7 @@
  */
 
 #include <glib/gi18n.h>
-#include <handy.h>
+#include <adwaita.h>
 #include <vte/vte.h>
 #define PCRE2_CODE_UNIT_WIDTH 0
 #include <pcre2.h>
@@ -90,17 +90,7 @@ kgx_terminal_dispose (GObject *object)
 
   g_clear_object (&self->actions);
   g_clear_pointer (&self->current_url, g_free);
-  g_clear_object (&self->long_press_gesture);
-
-  if (self->menu) {
-    gtk_menu_detach (GTK_MENU (self->menu));
-    self->menu = NULL;
-  }
-
-  if (self->touch_menu) {
-    gtk_popover_set_relative_to (GTK_POPOVER (self->touch_menu), NULL);
-    self->touch_menu = NULL;
-  }
+  g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
 
   G_OBJECT_CLASS (kgx_terminal_parent_class)->dispose (object);
 }
@@ -134,9 +124,9 @@ update_terminal_colors (KgxTerminal *self)
   };
 
   if (self->theme == KGX_THEME_AUTO) {
-    HdyStyleManager *manager = hdy_style_manager_get_default ();
+    AdwStyleManager *manager = adw_style_manager_get_default ();
 
-    if (hdy_style_manager_get_dark (manager)) {
+    if (adw_style_manager_get_dark (manager)) {
       resolved_theme = KGX_THEME_NIGHT;
     } else {
       resolved_theme = KGX_THEME_DAY;
@@ -247,7 +237,9 @@ kgx_terminal_get_property (GObject    *object,
 
 
 static gboolean
-have_url_under_pointer (KgxTerminal *self, GdkEvent *event)
+have_url_under_pointer (KgxTerminal *self,
+                        double       x,
+                        double       y)
 {
   g_autofree char *hyperlink = NULL;
   g_autofree char *match = NULL;
@@ -255,14 +247,12 @@ have_url_under_pointer (KgxTerminal *self, GdkEvent *event)
 
   g_clear_pointer (&self->current_url, g_free);
 
-  hyperlink = vte_terminal_hyperlink_check_event (VTE_TERMINAL (self), event);
+  hyperlink = vte_terminal_check_hyperlink_at (VTE_TERMINAL (self), x, y);
 
   if (G_UNLIKELY (hyperlink)) {
     self->current_url = g_steal_pointer (&hyperlink);
   } else {
-    match = vte_terminal_match_check_event (VTE_TERMINAL (self),
-                                            event,
-                                            &match_id);
+    match = vte_terminal_check_match_at (VTE_TERMINAL (self), x, y, &match_id);
 
     for (int i = 0; i < KGX_TERMINAL_N_LINK_REGEX; i++) {
       if (self->match_id[i] == match_id) {
@@ -277,123 +267,121 @@ have_url_under_pointer (KgxTerminal *self, GdkEvent *event)
 
 
 static void
-context_menu_detach (KgxTerminal *self,
-                     GtkMenu     *menu)
+update_menu_position (KgxTerminal *self)
 {
-  self->menu = NULL;
+  if (!self->popup_menu)
+    return;
+
+  gtk_popover_set_position (GTK_POPOVER (self->popup_menu),
+                            self->popup_is_touch ? GTK_POS_TOP : GTK_POS_BOTTOM);
+
+  gtk_popover_set_has_arrow (GTK_POPOVER (self->popup_menu), self->popup_is_touch);
+
+  if (self->popup_is_touch) {
+    gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_FILL);
+  } else if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) {
+    gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_END);
+  } else {
+    gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_START);
+  }
 }
 
 
 static void
-context_menu (GtkWidget *widget,
-              int        x,
-              int        y,
-              gboolean   touch,
-              GdkEvent  *event)
+context_menu (KgxTerminal *self,
+              double       x,
+              double       y,
+              gboolean     touch)
 {
-  KgxTerminal *self = KGX_TERMINAL (widget);
   GAction *act;
   GtkApplication *app;
-  GMenu *model;
   gboolean value;
 
-  value = have_url_under_pointer (self, event);
+  value = have_url_under_pointer (self, x, y);
 
   act = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "open-link");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (act), value);
   act = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "copy-link");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (act), value);
 
-  if (touch) {
-    GdkRectangle rect = {x, y, 1, 1};
-
-    if (!self->touch_menu) {
-      app = GTK_APPLICATION (g_application_get_default ());
-      model = gtk_application_get_menu_by_id (app, "context-menu");
+  app = GTK_APPLICATION (g_application_get_default ());
 
-      self->touch_menu = gtk_popover_new_from_model (widget, G_MENU_MODEL (model));
-    }
+  if (!self->popup_menu) {
+    GMenu *model = gtk_application_get_menu_by_id (app, "context-menu");
 
-    gtk_popover_set_pointing_to (GTK_POPOVER (self->touch_menu), &rect);
-    gtk_popover_popup (GTK_POPOVER (self->touch_menu));
-  } else {
-    if (!self->menu) {
-      app = GTK_APPLICATION (g_application_get_default ());
-      model = gtk_application_get_menu_by_id (app, "context-menu");
+    self->popup_menu = gtk_popover_menu_new_from_model (G_MENU_MODEL (model));
 
-      self->menu = gtk_menu_new_from_model (G_MENU_MODEL (model));
-      gtk_style_context_add_class (gtk_widget_get_style_context (self->menu),
-                                   GTK_STYLE_CLASS_CONTEXT_MENU);
+    gtk_widget_set_parent (self->popup_menu, GTK_WIDGET (self));
+  }
 
-      gtk_menu_attach_to_widget (GTK_MENU (self->menu), widget,
-                                 (GtkMenuDetachFunc) context_menu_detach);
-    }
+  self->popup_is_touch = touch;
 
-    if (event && gdk_event_triggers_context_menu (event)) {
-      gtk_menu_popup_at_pointer (GTK_MENU (self->menu), event);
-    } else {
-      gtk_menu_popup_at_widget (GTK_MENU (self->menu),
-                                widget,
-                                GDK_GRAVITY_SOUTH_WEST,
-                                GDK_GRAVITY_NORTH_WEST,
-                                event);
+  update_menu_position (self);
 
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (self->menu), FALSE);
-    }
+  if (x > -1 && y > -1) {
+    GdkRectangle rect = { x, y, 1, 1 };
+    gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), &rect);
+  } else {
+    gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), NULL);
   }
+
+  gtk_popover_popup (GTK_POPOVER (self->popup_menu));
 }
 
 
-static gboolean
-kgx_terminal_popup_menu (GtkWidget *self)
+static void
+menu_popup_activated (GtkWidget  *self,
+                      const char *action_name,
+                      GVariant   *parameters)
 {
-  context_menu (self, 1, 1, FALSE, NULL);
-
-  return TRUE;
+  context_menu (KGX_TERMINAL (self), 1, 1, FALSE);
 }
 
 
 static void
 open_link (KgxTerminal *self, guint32 timestamp)
 {
-  g_autoptr (GError) error = NULL;
-
-  gtk_show_uri_on_window (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
-                          self->current_url,
-                          timestamp,
-                          &error);
-
-  if (error) {
-    g_warning ("Failed to open link %s", error->message);
-  }
+  gtk_show_uri (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))),
+                self->current_url,
+                timestamp);
 }
 
 
-static gboolean
-kgx_terminal_button_press_event (GtkWidget *widget, GdkEventButton *event)
+static void
+kgx_terminal_size_allocate (GtkWidget *widget,
+                            int        width,
+                            int        height,
+                            int        baseline)
 {
+  int          rows;
+  int          cols;
   KgxTerminal *self = KGX_TERMINAL (widget);
-  GdkModifierType state;
+  VteTerminal *term = VTE_TERMINAL (self);
 
-  if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
-      event->type == GDK_BUTTON_PRESS) {
-    context_menu (widget, event->x, event->y, FALSE, (GdkEvent *) event);
+  if (self->popup_menu)
+    gtk_popover_present (GTK_POPOVER (self->popup_menu));
 
-    return TRUE;
-  }
+  GTK_WIDGET_CLASS (kgx_terminal_parent_class)->size_allocate (widget, width, height, baseline);
+
+  rows = vte_terminal_get_row_count (term);
+  cols = vte_terminal_get_column_count (term);
 
-  state = event->state & gtk_accelerator_get_default_mod_mask ();
+  g_signal_emit (self, signals[SIZE_CHANGED], 0, rows, cols);
+}
 
-  if (have_url_under_pointer (self, (GdkEvent *) event) &&
-      (event->button == 1 || event->button == 2) &&
-      state & GDK_CONTROL_MASK) {
-    open_link (self, event->time);
 
-    return GDK_EVENT_STOP;
-  }
+static void
+kgx_terminal_direction_changed (GtkWidget        *widget,
+                                GtkTextDirection  previous_direction)
+{
+  KgxTerminal *self = KGX_TERMINAL (widget);
+
+  GTK_WIDGET_CLASS (kgx_terminal_parent_class)->direction_changed (widget, previous_direction);
 
-  return GTK_WIDGET_CLASS (kgx_terminal_parent_class)->button_press_event (widget,
-                                                                           event);
+  if (gtk_widget_get_direction (widget) == previous_direction)
+    return;
+
+  update_menu_position (self);
 }
 
 
@@ -407,8 +395,8 @@ kgx_terminal_class_init (KgxTerminalClass *klass)
   object_class->set_property = kgx_terminal_set_property;
   object_class->get_property = kgx_terminal_get_property;
 
-  widget_class->popup_menu = kgx_terminal_popup_menu;
-  widget_class->button_press_event = kgx_terminal_button_press_event;
+  widget_class->size_allocate = kgx_terminal_size_allocate;
+  widget_class->direction_changed = kgx_terminal_direction_changed;
 
   /**
    * KgxTerminal:theme:
@@ -461,6 +449,25 @@ kgx_terminal_class_init (KgxTerminalClass *klass)
                                         2,
                                         G_TYPE_UINT,
                                         G_TYPE_UINT);
+
+  /**
+   * KgxTerminal|menu.popup:
+   *
+   * Opens the context menu.
+   */
+  gtk_widget_class_install_action (widget_class,
+                                   "menu.popup",
+                                   NULL,
+                                   menu_popup_activated);
+
+  gtk_widget_class_add_binding_action (widget_class,
+                                       GDK_KEY_F10, GDK_SHIFT_MASK,
+                                       "menu.popup",
+                                       NULL);
+  gtk_widget_class_add_binding_action (widget_class,
+                                       GDK_KEY_Menu, 0,
+                                       "menu.popup",
+                                       NULL);
 }
 
 
@@ -478,12 +485,10 @@ copy_link_activated (GSimpleAction *action,
                      GVariant      *parameter,
                      gpointer       data)
 {
-  GtkClipboard *cb;
   KgxTerminal *self = KGX_TERMINAL (data);
+  GdkClipboard *cb = gtk_widget_get_clipboard (GTK_WIDGET (self));
 
-  cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-  gtk_clipboard_set_text (cb, self->current_url, -1);
+  gdk_clipboard_set_text (cb, self->current_url);
 }
 
 static void
@@ -491,7 +496,10 @@ copy_activated (GSimpleAction *action,
                 GVariant      *parameter,
                 gpointer       data)
 {
-  vte_terminal_copy_clipboard_format (VTE_TERMINAL (data), VTE_FORMAT_TEXT);
+  GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
+  g_autofree char *text = vte_terminal_get_text_selected (VTE_TERMINAL (data),
+                                                          VTE_FORMAT_TEXT);
+  gdk_clipboard_set_text (clipboard, text);
 }
 
 
@@ -522,7 +530,7 @@ paste_response (GtkDialog *dlg,
   g_autoptr (PasteData) paste = data;
 
   if (dlg && GTK_IS_DIALOG (dlg)) {
-    gtk_widget_destroy (GTK_WIDGET (dlg));
+    gtk_window_destroy (GTK_WINDOW (dlg));
   }
 
   if (response == GTK_RESPONSE_ACCEPT) {
@@ -532,11 +540,20 @@ paste_response (GtkDialog *dlg,
 
 
 static void
-got_text (GtkClipboard *clipboard,
-          const char   *text,
-          gpointer      data)
+got_text (GdkClipboard *cb,
+          GAsyncResult *result,
+          KgxTerminal  *self)
 {
-  kgx_terminal_accept_paste (KGX_TERMINAL (data), text);
+  g_autofree char *text = NULL;
+  g_autoptr (GError) error = NULL;
+
+  /* Get the resulting text of the read operation */
+  text = gdk_clipboard_read_text_finish (cb, result, &error);
+
+  if (error)
+    g_critical ("Couldn't paste text: %s\n", error->message);
+
+  kgx_terminal_accept_paste (self, text);
 }
 
 
@@ -545,11 +562,9 @@ paste_activated (GSimpleAction *action,
                  GVariant      *parameter,
                  gpointer       data)
 {
-  GtkClipboard *cb;
-
-  cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+  GdkClipboard *cb = gtk_widget_get_clipboard (GTK_WIDGET (data));
 
-  gtk_clipboard_request_text (cb, got_text, data);
+  gdk_clipboard_read_text_async (cb, NULL, (GAsyncReadyCallback) got_text, data);
 }
 
 static void
@@ -628,13 +643,57 @@ static GActionEntry term_entries[] = {
 };
 
 
+static void
+pressed (GtkGestureClick *gesture,
+         int              n_presses,
+         double           x,
+         double           y,
+         KgxTerminal     *self)
+{
+  GdkEvent *event;
+  GdkModifierType state;
+  guint button;
+
+  if (n_presses > 1) {
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+    return;
+  }
+
+  event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (gesture));
+
+  if (gdk_event_triggers_context_menu (event)) {
+    context_menu (self, x, y, FALSE);
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
+    return;
+  }
+
+  state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+  button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+  if (have_url_under_pointer (self, x, y) &&
+      (button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) &&
+      state & GDK_CONTROL_MASK) {
+    guint32 time = gtk_event_controller_get_current_event_time (GTK_EVENT_CONTROLLER (gesture));
+
+    open_link (self, time);
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+
+    return;
+  }
+
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+}
+
+
 static void
 long_pressed (GtkGestureLongPress *gesture,
-              gdouble              x,
-              gdouble              y,
+              double               x,
+              double               y,
               KgxTerminal         *self)
 {
-  context_menu (GTK_WIDGET (self), (int) x, (int) y, TRUE, NULL);
+  context_menu (self, x, y, TRUE);
+  gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
 }
 
 
@@ -667,22 +726,6 @@ location_changed (KgxTerminal *self)
 }
 
 
-static void
-size_changed (GtkWidget    *widget,
-              GdkRectangle *allocation)
-{
-  int          rows;
-  int          cols;
-  KgxTerminal *self = KGX_TERMINAL (widget);
-  VteTerminal *term = VTE_TERMINAL (self);
-
-  rows = vte_terminal_get_row_count (term);
-  cols = vte_terminal_get_column_count (term);
-
-  g_signal_emit (self, signals[SIZE_CHANGED], 0, rows, cols);
-}
-
-
 static void
 dark_changed (KgxTerminal *self)
 {
@@ -707,12 +750,15 @@ kgx_terminal_init (KgxTerminal *self)
                                   "term",
                                   G_ACTION_GROUP (self->actions));
 
-  gesture = gtk_gesture_long_press_new (GTK_WIDGET (self));
+  gesture = gtk_gesture_click_new ();
+  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
+  g_signal_connect (gesture, "pressed", G_CALLBACK (pressed), self);
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
+
+  gesture = gtk_gesture_long_press_new ();
   gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), TRUE);
-  gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
-                                              GTK_PHASE_TARGET);
   g_signal_connect (gesture, "pressed", G_CALLBACK (long_pressed), self);
-  self->long_press_gesture = gesture;
+  gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
 
   act = g_action_map_lookup_action (self->actions, "open-link");
   g_simple_action_set_enabled (G_SIMPLE_ACTION (act), FALSE);
@@ -735,8 +781,6 @@ kgx_terminal_init (KgxTerminal *self)
                     G_CALLBACK (location_changed), NULL);
   g_signal_connect (self, "current-file-uri-changed",
                     G_CALLBACK (location_changed), NULL);
-  g_signal_connect (self, "size-allocate",
-                    G_CALLBACK (size_changed), NULL);
 
   for (int i = 0; i < KGX_TERMINAL_N_LINK_REGEX; i++) {
     g_autoptr (VteRegex) regex = NULL;
@@ -758,7 +802,7 @@ kgx_terminal_init (KgxTerminal *self)
                                         "pointer");
   }
 
-  g_signal_connect_object (hdy_style_manager_get_default (),
+  g_signal_connect_object (adw_style_manager_get_default (),
                            "notify::dark", G_CALLBACK (dark_changed),
                            self, G_CONNECT_SWAPPED);
 
@@ -786,7 +830,7 @@ kgx_terminal_accept_paste (KgxTerminal *self,
   if (g_strstr_len (striped, len, "sudo") != NULL &&
       g_strstr_len (striped, len, "\n") != NULL) {
     GtkWidget *accept = NULL;
-    GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+    GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))),
                                              GTK_DIALOG_MODAL,
                                              GTK_MESSAGE_QUESTION,
                                              GTK_BUTTONS_NONE,
@@ -806,8 +850,8 @@ kgx_terminal_accept_paste (KgxTerminal *self,
     accept = gtk_dialog_add_button (GTK_DIALOG (dlg),
                                     _("_Paste"),
                                     GTK_RESPONSE_ACCEPT);
-    gtk_style_context_add_class (gtk_widget_get_style_context (accept),
-                                 "destructive-action");
+    gtk_widget_add_css_class (accept, "destructive-action");
+
     gtk_widget_show (dlg);
   } else {
     paste_response (NULL, GTK_RESPONSE_ACCEPT, g_steal_pointer (&paste));
diff --git a/src/kgx-terminal.h b/src/kgx-terminal.h
index 73dd53f..72e11d6 100644
--- a/src/kgx-terminal.h
+++ b/src/kgx-terminal.h
@@ -101,17 +101,13 @@ struct _KgxTerminal {
   KgxTheme    theme;
   gboolean    opaque;
   GActionMap *actions;
+  GtkWidget  *popup_menu;
 
   /* Hyperlinks */
   char       *current_url;
   int         match_id[KGX_TERMINAL_N_LINK_REGEX];
 
-  /* Gestures */
-  GtkGesture *long_press_gesture;
-
-  /* Menus */
-  GtkWidget *menu;
-  GtkWidget *touch_menu;
+  gboolean    popup_is_touch;
 };
 
 G_DECLARE_FINAL_TYPE (KgxTerminal, kgx_terminal, KGX, TERMINAL, VteTerminal)
diff --git a/src/kgx-theme-switcher.c b/src/kgx-theme-switcher.c
index 7594ddb..04d2f62 100644
--- a/src/kgx-theme-switcher.c
+++ b/src/kgx-theme-switcher.c
@@ -23,10 +23,11 @@
 
 
 struct _KgxThemeSwitcher {
-  GtkBin   parent_instance;
+  GtkWidget parent_instance;
 
   KgxTheme theme;
 
+  GtkWidget *box;
   GtkWidget *system_selector;
   GtkWidget *light_selector;
   GtkWidget *dark_selector;
@@ -34,7 +35,7 @@ struct _KgxThemeSwitcher {
 };
 
 
-G_DEFINE_TYPE (KgxThemeSwitcher, kgx_theme_switcher, GTK_TYPE_BIN)
+G_DEFINE_TYPE (KgxThemeSwitcher, kgx_theme_switcher, GTK_TYPE_WIDGET)
 
 
 enum {
@@ -47,13 +48,13 @@ static GParamSpec *pspecs[LAST_PROP] = { NULL, };
 
 
 static void
-theme_radio_active_changed (KgxThemeSwitcher *self)
+theme_check_active_changed (KgxThemeSwitcher *self)
 {
   KgxTheme theme;
 
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->system_selector))) {
+  if (gtk_check_button_get_active (GTK_CHECK_BUTTON (self->system_selector))) {
       theme = KGX_THEME_AUTO;
-  } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->light_selector))) {
+  } else if (gtk_check_button_get_active (GTK_CHECK_BUTTON (self->light_selector))) {
       theme = KGX_THEME_DAY;
   } else {
       theme = KGX_THEME_NIGHT;
@@ -77,15 +78,15 @@ set_theme (KgxThemeSwitcher *self,
 
   switch (theme) {
     case KGX_THEME_AUTO:
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->system_selector), TRUE);
+      gtk_check_button_set_active (GTK_CHECK_BUTTON (self->system_selector), TRUE);
       break;
     case KGX_THEME_DAY:
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->light_selector), TRUE);
+      gtk_check_button_set_active (GTK_CHECK_BUTTON (self->light_selector), TRUE);
       break;
     case KGX_THEME_NIGHT:
     case KGX_THEME_HACKER:
     default:
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->dark_selector), TRUE);
+      gtk_check_button_set_active (GTK_CHECK_BUTTON (self->dark_selector), TRUE);
       break;
   }
 
@@ -136,6 +137,18 @@ kgx_theme_switcher_set_property (GObject      *object,
 }
 
 
+static void
+kgx_theme_switcher_dispose (GObject *object)
+{
+  KgxThemeSwitcher *self = KGX_THEME_SWITCHER (object);
+
+  if (self->box)
+    gtk_widget_unparent (self->box);
+
+  G_OBJECT_CLASS (kgx_theme_switcher_parent_class)->dispose (object);
+}
+
+
 static void
 kgx_theme_switcher_class_init (KgxThemeSwitcherClass *klass)
 {
@@ -144,6 +157,7 @@ kgx_theme_switcher_class_init (KgxThemeSwitcherClass *klass)
 
   object_class->get_property = kgx_theme_switcher_get_property;
   object_class->set_property = kgx_theme_switcher_set_property;
+  object_class->dispose = kgx_theme_switcher_dispose;
 
   pspecs[PROP_THEME] =
     g_param_spec_enum ("theme",
@@ -165,13 +179,15 @@ kgx_theme_switcher_class_init (KgxThemeSwitcherClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class,
                                                KGX_APPLICATION_PATH "kgx-theme-switcher.ui");
 
+  gtk_widget_class_bind_template_child (widget_class, KgxThemeSwitcher, box);
   gtk_widget_class_bind_template_child (widget_class, KgxThemeSwitcher, system_selector);
   gtk_widget_class_bind_template_child (widget_class, KgxThemeSwitcher, light_selector);
   gtk_widget_class_bind_template_child (widget_class, KgxThemeSwitcher, dark_selector);
 
-  gtk_widget_class_bind_template_callback (widget_class, theme_radio_active_changed);
+  gtk_widget_class_bind_template_callback (widget_class, theme_check_active_changed);
 
   gtk_widget_class_set_css_name (widget_class, "themeswitcher");
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
 }
 
 
diff --git a/src/kgx-theme-switcher.h b/src/kgx-theme-switcher.h
index 848b2f7..2acf158 100644
--- a/src/kgx-theme-switcher.h
+++ b/src/kgx-theme-switcher.h
@@ -24,6 +24,6 @@ G_BEGIN_DECLS
 
 #define KGX_TYPE_THEME_SWITCHER (kgx_theme_switcher_get_type())
 
-G_DECLARE_FINAL_TYPE (KgxThemeSwitcher, kgx_theme_switcher, KGX, THEME_SWITCHER, GtkBin)
+G_DECLARE_FINAL_TYPE (KgxThemeSwitcher, kgx_theme_switcher, KGX, THEME_SWITCHER, GtkWidget)
 
 G_END_DECLS
diff --git a/src/kgx-theme-switcher.ui b/src/kgx-theme-switcher.ui
index aa06b6d..7e1abc4 100644
--- a/src/kgx-theme-switcher.ui
+++ b/src/kgx-theme-switcher.ui
@@ -1,10 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
-  <template class="KgxThemeSwitcher" parent="GtkBin">
+  <requires lib="gtk" version="4.0"/>
+  <template class="KgxThemeSwitcher" parent="GtkWidget">
     <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
+      <object class="GtkBox" id="box">
         <property name="orientation">horizontal</property>
         <property name="homogeneous">True</property>
         <property name="spacing">18</property>
@@ -13,10 +12,10 @@
             <property name="visible" bind-source="KgxThemeSwitcher" bind-property="show-system" 
bind-flags="sync-create"/>
             <property name="halign">center</property>
             <child>
-              <object class="GtkRadioButton" id="system_selector">
-                <property name="visible">True</property>
+              <object class="GtkCheckButton" id="system_selector">
                 <property name="tooltip-text" translatable="yes">Use System Colors</property>
-                <signal name="notify::active" handler="theme_radio_active_changed" swapped="yes"/>
+                <property name="active">True</property>
+                <signal name="notify::active" handler="theme_check_active_changed" swapped="yes"/>
                 <style>
                   <class name="system"/>
                 </style>
@@ -38,14 +37,12 @@
         </child>
         <child>
           <object class="GtkOverlay">
-            <property name="visible">True</property>
             <property name="halign">center</property>
             <child>
-              <object class="GtkRadioButton" id="light_selector">
-                <property name="visible">True</property>
+              <object class="GtkCheckButton" id="light_selector">
                 <property name="group">system_selector</property>
                 <property name="tooltip-text" translatable="yes">Use Light Colors</property>
-                <signal name="notify::active" handler="theme_radio_active_changed" swapped="yes"/>
+                <signal name="notify::active" handler="theme_check_active_changed" swapped="yes"/>
                 <style>
                   <class name="light"/>
                 </style>
@@ -67,15 +64,13 @@
         </child>
         <child>
           <object class="GtkOverlay">
-            <property name="visible">True</property>
             <property name="halign">center</property>
             <child>
-              <object class="GtkRadioButton" id="dark_selector">
-                <property name="visible">True</property>
+              <object class="GtkCheckButton" id="dark_selector">
                 <property name="group">system_selector</property>
                 <property name="tooltip-text" translatable="yes">Use Dark Colors</property>
                 <property name="halign">center</property>
-                <signal name="notify::active" handler="theme_radio_active_changed" swapped="yes"/>
+                <signal name="notify::active" handler="theme_check_active_changed" swapped="yes"/>
                 <style>
                   <class name="dark"/>
                 </style>
diff --git a/src/kgx-window.c b/src/kgx-window.c
index d8fe17a..1970bff 100644
--- a/src/kgx-window.c
+++ b/src/kgx-window.c
@@ -21,7 +21,7 @@
  * @title: KgxWindow
  * @short_description: Window
  *
- * The main #HdyApplicationWindow that acts as the terminal
+ * The main #AdwApplicationWindow that acts as the terminal
  */
 
 #include "kgx-config.h"
@@ -29,7 +29,7 @@
 #include <glib/gi18n.h>
 #include <vte/vte.h>
 #include <math.h>
-#include <handy.h>
+#include <adwaita.h>
 
 #include "rgba.h"
 
@@ -42,7 +42,7 @@
 #include "kgx-tab-switcher.h"
 #include "kgx-theme-switcher.h"
 
-G_DEFINE_TYPE (KgxWindow, kgx_window, HDY_TYPE_APPLICATION_WINDOW)
+G_DEFINE_TYPE (KgxWindow, kgx_window, ADW_TYPE_APPLICATION_WINDOW)
 
 enum {
   PROP_0,
@@ -84,12 +84,12 @@ kgx_window_constructed (GObject *object)
 {
   KgxWindow       *self = KGX_WINDOW (object);
   GtkApplication  *application = NULL;
-  HdyStyleManager *style_manager;
+  AdwStyleManager *style_manager;
 
   G_OBJECT_CLASS (kgx_window_parent_class)->constructed (object);
 
   application = gtk_window_get_application (GTK_WINDOW (self));
-  style_manager = hdy_style_manager_get_default ();
+  style_manager = adw_style_manager_get_default ();
 
   g_object_bind_property (application, "theme",
                           self->pages, "theme",
@@ -179,21 +179,20 @@ delete_response (GtkWidget *dlg,
                  int        response,
                  KgxWindow *self)
 {
-  gtk_widget_destroy (dlg);
+  gtk_window_destroy (GTK_WINDOW (dlg));
 
   if (response == GTK_RESPONSE_OK) {
     self->close_anyway = TRUE;
 
-    gtk_widget_destroy (GTK_WIDGET (self));
+    gtk_window_destroy (GTK_WINDOW (self));
   }
 }
 
 
 static gboolean
-kgx_window_delete_event (GtkWidget   *widget,
-                         GdkEventAny *event)
+kgx_window_close_request (GtkWindow *window)
 {
-  KgxWindow *self = KGX_WINDOW (widget);
+  KgxWindow *self = KGX_WINDOW (window);
   GtkWidget *dlg;
   g_autoptr (GPtrArray) children = NULL;
 
@@ -230,51 +229,32 @@ active_changed (GObject *object, GParamSpec *pspec, gpointer data)
 }
 
 
-static gboolean
-key_press_event (GtkWidget   *widget,
-                 GdkEventKey *event,
-                 KgxWindow   *self)
+static void
+state_or_size_changed (KgxWindow  *self)
 {
-  GdkModifierType default_modifiers = gtk_accelerator_get_default_mod_mask ();
-  guint keyval;
-  GdkModifierType state;
-  GdkModifierType consumed;
-  GdkKeymap *keymap;
-
-  gdk_event_get_state ((GdkEvent *) event, &state);
-
-  keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
+  GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (self));
+  GdkToplevelState state = gdk_toplevel_get_state (GDK_TOPLEVEL (surface));
 
-  gdk_keymap_translate_keyboard_state (keymap,
-                                       event->hardware_keycode,
-                                       state,
-                                       event->group,
-                                       &keyval, NULL, NULL, &consumed);
+  self->is_maximized_or_tiled =
+    (state & (GDK_TOPLEVEL_STATE_FULLSCREEN |
+              GDK_TOPLEVEL_STATE_MAXIMIZED |
+              GDK_TOPLEVEL_STATE_TILED |
+              GDK_TOPLEVEL_STATE_TOP_TILED |
+              GDK_TOPLEVEL_STATE_RIGHT_TILED |
+              GDK_TOPLEVEL_STATE_BOTTOM_TILED |
+              GDK_TOPLEVEL_STATE_LEFT_TILED)) > 0;
 
-  state &= ~consumed & default_modifiers;
+  g_object_set (self->pages, "opaque", self->is_maximized_or_tiled, NULL);
 
-  /* Override some shortcuts from HdyTabView:shortcut-widget back, as they are
-   * needed for terminal apps. This should be fixed on libhandy side, but would
-   * likely require API changes, so carry this for now.
-   *
-   * See https://gitlab.gnome.org/GNOME/libhandy/-/issues/422
-   */
-  if ((((keyval == GDK_KEY_Page_Up ||
-         keyval == GDK_KEY_KP_Page_Up ||
-         keyval == GDK_KEY_Page_Down ||
-         keyval == GDK_KEY_KP_Page_Down) &&
-        kgx_pages_count (KGX_PAGES (self->pages)) <= 1) ||
-       keyval == GDK_KEY_Home ||
-       keyval == GDK_KEY_KP_Home ||
-       keyval == GDK_KEY_End ||
-       keyval == GDK_KEY_KP_End) &&
-      (state == GDK_CONTROL_MASK ||
-       state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) {
-    return kgx_pages_key_press_event (KGX_PAGES (self->pages),
-                                      (GdkEvent *) event);
+  if (self->is_maximized_or_tiled) {
+    gtk_widget_add_css_class (GTK_WIDGET (self), "opaque");
+  } else {
+    gtk_widget_remove_css_class (GTK_WIDGET (self), "opaque");
   }
 
-  return GDK_EVENT_PROPAGATE;
+  gtk_window_get_default_size (GTK_WINDOW (self),
+                               &self->current_width,
+                               &self->current_height);
 }
 
 
@@ -304,39 +284,33 @@ static void
 status_changed (GObject *object, GParamSpec *pspec, gpointer data)
 {
   KgxWindow *self = KGX_WINDOW (object);
-  GtkStyleContext *context;
   KgxStatus status;
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (self));
-
   status = kgx_pages_current_status (KGX_PAGES (self->pages));
 
   if (status & KGX_REMOTE) {
-    gtk_style_context_add_class (context, KGX_WINDOW_STYLE_REMOTE);
+    gtk_widget_add_css_class (GTK_WIDGET (self), KGX_WINDOW_STYLE_REMOTE);
   } else {
-    gtk_style_context_remove_class (context, KGX_WINDOW_STYLE_REMOTE);
+    gtk_widget_remove_css_class (GTK_WIDGET (self), KGX_WINDOW_STYLE_REMOTE);
   }
 
   if (status & KGX_PRIVILEGED) {
-    gtk_style_context_add_class (context, KGX_WINDOW_STYLE_ROOT);
+    gtk_widget_add_css_class (GTK_WIDGET (self), KGX_WINDOW_STYLE_ROOT);
   } else {
-    gtk_style_context_remove_class (context, KGX_WINDOW_STYLE_ROOT);
+    gtk_widget_remove_css_class (GTK_WIDGET (self), KGX_WINDOW_STYLE_ROOT);
   }
 }
 
 
 static void
-extra_drag_data_received (HdyTabBar        *bar,
-                          HdyTabPage       *page,
-                          GdkDragContext   *context,
-                          GtkSelectionData *selection_data,
-                          guint             info,
-                          guint             time,
-                          KgxWindow        *self)
+extra_drag_drop (AdwTabBar        *bar,
+                 AdwTabPage       *page,
+                 GValue           *value,
+                 KgxWindow        *self)
 {
-  KgxTab *tab = KGX_TAB (hdy_tab_page_get_child (page));
+  KgxTab *tab = KGX_TAB (adw_tab_page_get_child (page));
 
-  kgx_tab_accept_drop (tab, selection_data);
+  kgx_tab_accept_drop (tab, value);
 }
 
 
@@ -353,44 +327,41 @@ new_tab_cb (KgxTabSwitcher *switcher,
 }
 
 
-static gboolean
-kgx_window_window_state_event (GtkWidget           *widget,
-                               GdkEventWindowState *event)
+static void
+kgx_window_realize (GtkWidget *widget)
 {
   KgxWindow *self = KGX_WINDOW (widget);
+  GdkSurface *surface;
 
-  self->is_maximized_or_tiled =
-    (event->new_window_state & (GDK_WINDOW_STATE_FULLSCREEN |
-                                GDK_WINDOW_STATE_MAXIMIZED |
-                                GDK_WINDOW_STATE_TILED |
-                                GDK_WINDOW_STATE_TOP_TILED |
-                                GDK_WINDOW_STATE_RIGHT_TILED |
-                                GDK_WINDOW_STATE_BOTTOM_TILED |
-                                GDK_WINDOW_STATE_LEFT_TILED)) > 0;
+  GTK_WIDGET_CLASS (kgx_window_parent_class)->realize (widget);
 
-  g_object_set (self->pages, "opaque", self->is_maximized_or_tiled, NULL);
+  surface = gtk_native_get_surface (GTK_NATIVE (self));
 
-  if (self->is_maximized_or_tiled)
-    gtk_style_context_add_class (gtk_widget_get_style_context (widget), "opaque");
-  else
-    gtk_style_context_remove_class (gtk_widget_get_style_context (widget), "opaque");
+  g_signal_connect_swapped (surface, "notify::state",
+                            G_CALLBACK (state_or_size_changed), self);
+  g_signal_connect_swapped (self, "notify::default-width",
+                            G_CALLBACK (state_or_size_changed), self);
+  g_signal_connect_swapped (self, "notify::default-height",
+                            G_CALLBACK (state_or_size_changed), self);
 
-  return GTK_WIDGET_CLASS (kgx_window_parent_class)->window_state_event (widget, event);
+  state_or_size_changed (self);
 }
 
 
 static void
-kgx_window_size_allocate (GtkWidget     *widget,
-                          GtkAllocation *alloc)
+kgx_window_unrealize (GtkWidget *widget)
 {
   KgxWindow *self = KGX_WINDOW (widget);
+  GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (self));
 
-  GTK_WIDGET_CLASS (kgx_window_parent_class)->size_allocate (widget, alloc);
+  g_signal_handlers_disconnect_by_func (surface,
+                                        G_CALLBACK (state_or_size_changed),
+                                        self);
+  g_signal_handlers_disconnect_by_func (self,
+                                        G_CALLBACK (state_or_size_changed),
+                                        self);
 
-  if (!self->is_maximized_or_tiled)
-    gtk_window_get_size (GTK_WINDOW (self),
-                         &self->current_width,
-                         &self->current_height);
+  GTK_WIDGET_CLASS (kgx_window_parent_class)->unrealize (widget);
 }
 
 
@@ -399,15 +370,17 @@ kgx_window_class_init (KgxWindowClass *klass)
 {
   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass);
 
   object_class->constructed = kgx_window_constructed;
   object_class->dispose = kgx_window_dispose;
   object_class->set_property = kgx_window_set_property;
   object_class->get_property = kgx_window_get_property;
 
-  widget_class->delete_event = kgx_window_delete_event;
-  widget_class->window_state_event = kgx_window_window_state_event;
-  widget_class->size_allocate = kgx_window_size_allocate;
+  widget_class->realize = kgx_window_realize;
+  widget_class->unrealize = kgx_window_unrealize;
+
+  window_class->close_request = kgx_window_close_request;
 
   /**
    * KgxWindow:application:
@@ -426,7 +399,7 @@ kgx_window_class_init (KgxWindowClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class,
                                                KGX_APPLICATION_PATH "kgx-window.ui");
 
-  gtk_widget_class_bind_template_child (widget_class, KgxWindow, header_bar);
+  gtk_widget_class_bind_template_child (widget_class, KgxWindow, window_title);
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, exit_info);
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, exit_message);
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, theme_switcher);
@@ -435,13 +408,13 @@ kgx_window_class_init (KgxWindowClass *klass)
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, tab_button);
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, tab_switcher);
   gtk_widget_class_bind_template_child (widget_class, KgxWindow, pages);
+  gtk_widget_class_bind_template_child (widget_class, KgxWindow, primary_menu);
 
   gtk_widget_class_bind_template_callback (widget_class, active_changed);
-  gtk_widget_class_bind_template_callback (widget_class, key_press_event);
 
   gtk_widget_class_bind_template_callback (widget_class, zoom);
   gtk_widget_class_bind_template_callback (widget_class, status_changed);
-  gtk_widget_class_bind_template_callback (widget_class, extra_drag_data_received);
+  gtk_widget_class_bind_template_callback (widget_class, extra_drag_drop);
   gtk_widget_class_bind_template_callback (widget_class, new_tab_cb);
 }
 
@@ -625,11 +598,7 @@ static void
 kgx_window_init (KgxWindow *self)
 {
   g_autoptr (GtkWindowGroup) group = NULL;
-  g_autoptr (GtkTargetList) target_list = NULL;
   g_autoptr (GPropertyAction) pact = NULL;
-  #ifdef IS_DEVEL
-  GtkStyleContext *context;
-  #endif
 
   g_type_ensure (KGX_TYPE_TAB_BUTTON);
   g_type_ensure (KGX_TYPE_TAB_SWITCHER);
@@ -648,8 +617,7 @@ kgx_window_init (KgxWindow *self)
   g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (pact));
 
   #ifdef IS_DEVEL
-  context = gtk_widget_get_style_context (GTK_WIDGET (self));
-  gtk_style_context_add_class (context, "devel");
+  gtk_widget_add_css_class (GTK_WIDGET (self), "devel");
   #endif
 
   g_object_bind_property_full (self->pages, "title",
@@ -659,11 +627,11 @@ kgx_window_init (KgxWindow *self)
                                NULL, NULL, NULL);
 
   g_object_bind_property (self, "title",
-                          self->header_bar, "title",
+                          self->window_title, "title",
                           G_BINDING_SYNC_CREATE);
 
   g_object_bind_property_full (self->pages, "path",
-                               self->header_bar, "subtitle",
+                               self->window_title, "subtitle",
                                G_BINDING_SYNC_CREATE,
                                update_subtitle,
                                NULL, NULL, NULL);
@@ -678,17 +646,13 @@ kgx_window_init (KgxWindow *self)
                           self->tab_switcher, "view",
                           G_BINDING_SYNC_CREATE);
 
-  target_list = gtk_target_list_new (NULL, 0);
-  gtk_target_list_add_text_targets (target_list, 0);
-
-  hdy_tab_bar_set_extra_drag_dest_targets (HDY_TAB_BAR (self->tab_bar),
-                                           target_list);
+  adw_tab_bar_setup_extra_drop_target (ADW_TAB_BAR (self->tab_bar),
+                                       GDK_ACTION_COPY,
+                                       (GType[1]) { G_TYPE_STRING }, 1);
 
   group = gtk_window_group_new ();
   gtk_window_group_add_window (group, GTK_WINDOW (self));
 
-  kgx_pages_set_shortcut_widget (KGX_PAGES (self->pages), GTK_WIDGET (self));
-
   self->tab_actions = G_ACTION_MAP (g_simple_action_group_new ());
   g_action_map_add_action_entries (self->tab_actions,
                                    tab_entries,
diff --git a/src/kgx-window.h b/src/kgx-window.h
index c79661c..94bff51 100644
--- a/src/kgx-window.h
+++ b/src/kgx-window.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include <gtk/gtk.h>
-#include "handy.h"
+#include <adwaita.h>
 
 #include "kgx-terminal.h"
 #include "kgx-process.h"
@@ -61,7 +61,7 @@ typedef enum /*< enum,prefix=KGX >*/
  * @last_rows: the row count last time we received #GtkWidget::size-allocate
  * @timeout: the id of the #GSource used to hide the statusbar
  * @close_anyway: ignore running children and close without prompt
- * @header_bar: the #HdyHeaderBar that the styles are applied to
+ * @header_bar: the #GtkHeaderBar that the styles are applied to
  * @search_entry: the #GtkSearchEntry inside @search_bar
  * @search_bar: the windows #GtkSearchBar
  * @exit_info: the #GtkRevealer hat wraps @exit_message
@@ -73,7 +73,7 @@ typedef enum /*< enum,prefix=KGX >*/
 struct _KgxWindow
 {
   /*< private >*/
-  HdyApplicationWindow  parent_instance;
+  AdwApplicationWindow  parent_instance;
 
   /*< public >*/
   KgxTheme              theme;
@@ -86,16 +86,16 @@ struct _KgxWindow
   gboolean              close_anyway;
 
   /* Template widgets */
-  GtkWidget            *header_bar;
+  GtkWidget            *window_title;
   GtkWidget            *exit_info;
   GtkWidget            *exit_message;
   GtkWidget            *theme_switcher;
   GtkWidget            *zoom_level;
-  GtkWidget            *about_item;
   GtkWidget            *tab_bar;
   GtkWidget            *tab_button;
   GtkWidget            *tab_switcher;
   GtkWidget            *pages;
+  GMenu                *primary_menu;
 
   int                   current_width;
   int                   current_height;
@@ -104,7 +104,7 @@ struct _KgxWindow
   GActionMap           *tab_actions;
 };
 
-G_DECLARE_FINAL_TYPE (KgxWindow, kgx_window, KGX, WINDOW, HdyApplicationWindow)
+G_DECLARE_FINAL_TYPE (KgxWindow, kgx_window, KGX, WINDOW, AdwApplicationWindow)
 
 GFile      *kgx_window_get_working_dir (KgxWindow    *self);
 void        kgx_window_show_status     (KgxWindow    *self,
diff --git a/src/kgx-window.ui b/src/kgx-window.ui
index be7ad83..b3ad88d 100644
--- a/src/kgx-window.ui
+++ b/src/kgx-window.ui
@@ -1,199 +1,138 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
-  <object class="GtkPopoverMenu" id="popover">
-    <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="margin">10</property>
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="KgxThemeSwitcher" id="theme_switcher">
-            <property name="visible">True</property>
-            <property name="margin-bottom">12</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="visible">True</property>
-            <property name="orientation">horizontal</property>
-            <property name="margin-bottom">6</property>
-            <property name="homogeneous">True</property>
-            <child>
-              <object class="GtkButton">
-                <property name="visible">True</property>
-                <property name="action-name">app.zoom-out</property>
-                <property name="tooltip-text" translatable="yes">Shrink Text</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">zoom-out-symbolic</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="visible">True</property>
-                <property name="action-name">app.zoom-normal</property>
-                <property name="tooltip-text" translatable="yes">Reset Size</property>
-                <child>
-                  <object class="GtkLabel" id="zoom_level">
-                    <property name="visible">True</property>
-                    <property name="width_chars">5</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkButton">
-                <property name="visible">True</property>
-                <property name="action-name">app.zoom-in</property>
-                <property name="tooltip-text" translatable="yes">Enlarge Text</property>
-                <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">zoom-in-symbolic</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <style>
-              <class name="linked"/>
-            </style>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="visible">True</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="visible">True</property>
-            <property name="text" translatable="yes">_New Window</property>
-            <property name="action-name">win.new-window</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="visible">True</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton">
-            <property name="visible">True</property>
-            <property name="text" translatable="yes">_Keyboard Shortcuts</property>
-            <property name="action-name">win.show-help-overlay</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkModelButton" id="about_item">
-            <property name="visible">True</property>
-            <property name="text" translatable="yes">_About Console</property>
-            <property name="tooltip-text" translatable="yes">About this Program</property>
-            <property name="action-name">win.about</property>
-          </object>
-        </child>
-      </object>
-    </child>
-  </object>
-  <template class="KgxWindow" parent="HdyApplicationWindow">
+  <requires lib="gtk" version="4.0"/>
+  <menu id="primary_menu">
+    <section>
+      <item>
+        <attribute name="custom">theme-switcher</attribute>
+      </item>
+      <item>
+        <attribute name="custom">zoom-controls</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_New Window</attribute>
+        <attribute name="action">win.new-window</attribute>
+      </item>
+    </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
+        <attribute name="action">win.show-help-overlay</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_About Console</attribute>
+        <attribute name="action">win.about</attribute>
+      </item>
+    </section>
+  </menu>
+  <template class="KgxWindow" parent="AdwApplicationWindow">
     <signal name="notify::is-active" handler="active_changed" swapped="no"/>
-    <signal name="key-press-event" handler="key_press_event" swapped="no"/>
-    <child>
+    <property name="content">
       <object class="KgxTabSwitcher" id="tab_switcher">
-        <property name="visible">1</property>
         <signal name="new-tab" handler="new_tab_cb" swapped="no"/>
-        <child>
+        <property name="child">
           <object class="GtkBox">
-            <property name="visible">1</property>
             <property name="orientation">vertical</property>
             <child>
-              <object class="HdyHeaderBar" id="header_bar">
-                <property name="visible">1</property>
-                <property name="title" translatable="yes">King’s Cross</property>
-                <property name="show-close-button">1</property>
-                <child>
+              <object class="GtkHeaderBar" id="header_bar">
+                <property name="title-widget">
+                  <object class="AdwWindowTitle" id="window_title">
+                    <property name="title" translatable="yes">King’s Cross</property>
+                  </object>
+                </property>
+                <child type="start">
                   <object class="GtkToggleButton">
-                    <property name="visible">1</property>
                     <property name="can-focus">0</property>
                     <property name="receives-default">0</property>
                     <property name="action-name">win.find</property>
                     <property name="tooltip-text" translatable="yes">Find in Terminal</property>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">1</property>
-                        <property name="icon-name">edit-find-symbolic</property>
-                      </object>
-                    </child>
+                    <property name="icon-name">edit-find-symbolic</property>
                   </object>
-                  <packing>
-                    <property name="pack-type">start</property>
-                  </packing>
                 </child>
-                <child>
+                <child type="end">
                   <object class="GtkMenuButton">
-                    <property name="visible">1</property>
                     <property name="can-focus">0</property>
                     <property name="receives-default">1</property>
-                    <property name="popover">popover</property>
                     <property name="tooltip-text" translatable="yes">Menu</property>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">1</property>
-                        <property name="icon-name">open-menu-symbolic</property>
+                    <property name="icon-name">open-menu-symbolic</property>
+                    <property name="popover">
+                      <object class="GtkPopoverMenu">
+                        <property name="menu-model">primary_menu</property>
+                        <child type="theme-switcher">
+                          <object class="KgxThemeSwitcher" id="theme_switcher">
+                            <property name="margin-bottom">6</property>
+                          </object>
+                        </child>
+                        <child type="zoom-controls">
+                          <object class="GtkBox">
+                            <property name="orientation">horizontal</property>
+                            <property name="homogeneous">True</property>
+                            <child>
+                              <object class="GtkButton">
+                                <property name="action-name">app.zoom-out</property>
+                                <property name="icon-name">zoom-out-symbolic</property>
+                                <property name="tooltip-text" translatable="yes">Shrink Text</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkButton">
+                                <property name="action-name">app.zoom-normal</property>
+                                <property name="tooltip-text" translatable="yes">Reset Size</property>
+                                <child>
+                                  <object class="GtkLabel" id="zoom_level">
+                                    <property name="width_chars">5</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkButton">
+                                <property name="action-name">app.zoom-in</property>
+                                <property name="icon-name">zoom-in-symbolic</property>
+                                <property name="tooltip-text" translatable="yes">Enlarge Text</property>
+                              </object>
+                            </child>
+                            <style>
+                              <class name="linked"/>
+                            </style>
+                          </object>
+                        </child>
                       </object>
-                    </child>
+                    </property>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
-                <child>
+                <child type="end">
                   <object class="KgxTabButton" id="tab_button">
                     <property name="visible" bind-source="tab_switcher" bind-property="narrow" 
bind-flags="sync-create"/>
                     <property name="action-name">win.tab-switcher</property>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
-                <child>
+                <child type="end">
                   <object class="GtkButton">
                     <property name="visible" bind-source="tab_switcher" bind-property="narrow" 
bind-flags="sync-create|invert-boolean"/>
                     <property name="can-focus">0</property>
                     <property name="receives-default">0</property>
                     <property name="action-name">win.new-tab</property>
                     <property name="tooltip-text" translatable="yes">New Tab</property>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="visible">1</property>
-                        <property name="icon-name">tab-new-symbolic</property>
-                      </object>
-                    </child>
+                    <property name="icon-name">tab-new-symbolic</property>
                   </object>
-                  <packing>
-                    <property name="pack-type">end</property>
-                  </packing>
                 </child>
               </object>
             </child>
             <child>
               <object class="GtkRevealer">
-                <property name="visible">1</property>
                 <property name="reveal-child" bind-source="tab_switcher" bind-property="narrow" 
bind-flags="sync-create|invert-boolean"/>
-                <child>
-                  <object class="HdyTabBar" id="tab_bar">
-                    <property name="visible">1</property>
-                    <signal name="extra-drag-data-received" handler="extra_drag_data_received" swapped="no"/>
+                <property name="child">
+                  <object class="AdwTabBar" id="tab_bar">
+                    <signal name="extra-drag-drop" handler="extra_drag_drop" swapped="no"/>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
             <child>
               <object class="KgxPages" id="pages">
-                <property name="visible">1</property>
                 <property name="is-active" bind-source="KgxWindow" bind-property="is-active" 
bind-flags="sync-create" />
                 <signal name="zoom" handler="zoom" swapped="no"/>
                 <signal name="notify::status" handler="status_changed" swapped="yes" />
@@ -201,15 +140,13 @@
             </child>
             <child>
               <object class="GtkRevealer" id="exit_info">
-                <property name="visible">1</property>
                 <property name="reveal_child">False</property>
-                <child>
+                <property name="child">
                   <object class="GtkBox">
-                    <property name="visible">1</property>
+                    <property name="can_focus">False</property>
                     <property name="valign">end</property>
                     <child>
                       <object class="GtkLabel" id="exit_message">
-                        <property name="visible">True</property>
                         <property name="halign">start</property>
                         <property name="hexpand">True</property>
                       </object>
@@ -218,13 +155,13 @@
                       <class name="exit-info"/>
                     </style>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
           </object>
-        </child>
+        </property>
       </object>
-    </child>
+    </property>
     <style>
       <class name="terminal-window"/>
     </style>
diff --git a/src/kgx.gresource.xml.in b/src/kgx.gresource.xml.in
index c5b0414..bb82543 100644
--- a/src/kgx.gresource.xml.in
+++ b/src/kgx.gresource.xml.in
@@ -12,8 +12,6 @@
     <file compressed="true">kgx-simple-tab.ui</file>
     <file compressed="true">styles-light.css</file>
     <file compressed="true">styles-dark.css</file>
-    <file compressed="true">styles-hc.css</file>
-    <file compressed="true">styles-hc-dark.css</file>
     <file compressed="true">logo.txt</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/status-privileged-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/status-regular-symbolic.svg</file>
diff --git a/src/menus.ui b/src/menus.ui
index 53d115a..fd6ff18 100644
--- a/src/menus.ui
+++ b/src/menus.ui
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="3.20"/>
+  <requires lib="gtk" version="4.0"/>
   <menu id="context-menu">
     <section>
       <item>
diff --git a/src/meson.build b/src/meson.build
index f1a16be..0820d73 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -35,9 +35,9 @@ kgx_sources = [
 
 kgx_deps = [
   dependency('gio-2.0', version: '>= 2.66'),
-  dependency('gtk+-3.0', version: '>= 3.24'),
-  dependency('libhandy-1', version: '>= 1.5', fallback: ['handy', 'libhandy_dep']),
-  dependency('vte-2.91', version: '>= 0.67', fallback: ['vte', 'libvte_gtk3_dep']),
+  dependency('gtk4'),
+  dependency('libadwaita-1', version: '>= 1.2.alpha', fallback: ['adwaita', 'libadwaita_dep']),
+  dependency('vte-2.91-gtk4', version: '>= 0.69.91', fallback: ['vte', 'libvte_gtk4_dep']),
   dependency('libgtop-2.0'),
   dependency('libpcre2-8', version: '>= 10.32'),
   dependency('gsettings-desktop-schemas'),
@@ -66,8 +66,14 @@ kgx_enums = gnome.mkenums_simple('kgx-enums',
                                       'kgx-tab.h'
                                     ])
 
+kgx_cargs = [
+  '-DGDK_VERSION_MIN_REQUIRED=@0@'.format(gtk_ver),
+  '-DGDK_VERSION_MAX_ALLOWED=@0@'.format(gtk_ver)
+]
+
 kgx_lib = static_library (bin_name,
                    kgx_sources + kgx_enums,
+           c_args: kgx_cargs,
      dependencies: kgx_deps,
 )
 kgx_inc = include_directories('.')
@@ -80,5 +86,6 @@ kgx_dep = declare_dependency (sources: [config_h, kgx_enums],
 executable(        bin_name,
                    'main.c',
      dependencies: kgx_dep,
+           c_args: kgx_cargs,
           install: true,
 )
diff --git a/src/styles/_styles.scss b/src/styles/_styles.scss
index 049ecb2..b582dcd 100644
--- a/src/styles/_styles.scss
+++ b/src/styles/_styles.scss
@@ -1,56 +1,52 @@
-@import 'definitions';
-@import 'drawing';
+@function themecolor($s) {
+  @return unquote("@" + "#{$s}");
+}
+
+@function gtkalpha($c,$a) {
+  @return unquote("alpha(#{$c},#{$a})");
+}
+
+@mixin colored_header($bg, $fg, $backdrop) {
+  headerbar,
+  searchbar > revealer > box,
+  tabbar > revealer > box {
+    background-color: $bg;
+    color: $fg;
+
+    &:backdrop {
+      background-color: $backdrop;
+    }
+  }
+}
 
 .terminal-window {
   background: transparent;
 
   &.root {
-    $variant: 'dark';
-    $window_color: darken(#a51d2d, 15%);
-    @import 'recoloring';
+    @include colored_header(darken(#a51d2d, 15%), white, darken(#a51d2d, 18%));
   }
 
   &.remote {
-    $variant: 'dark';
-    $window_color: darken(#613583, 10%);
-    @import 'recoloring';
-  }
-
-  scrolledwindow.terminal overshoot {
-    $sides: "top", "bottom", "left", "right";
-
-    @each $side in $sides {
-      &.#{$side} {
-        @include overshoot($side, normal, #cccccc);
-
-        &:backdrop { @include overshoot($side, backdrop, #cccccc); }
-      }
-    }
+    @include colored_header(darken(#613583, 10%), white, darken(#613583, 13%));
   }
 }
 
 // Taken from nautilus
 .floating-bar {
   padding: 6px;
-  background-color: themecolor(theme_base_color);
-  border-width: 1px;
-  border-style: solid solid none;
-  background-clip: padding-box;
-  border-color: transparentize(black, if($variant == 'light', .8, .25));
-  border-radius: 6px 6px 0 0;
-
-  &:backdrop {
-    background-color: themecolor(theme_unfocused_base_color);
-  }
+  background-color: themecolor(view_bg_color);
+  color: themecolor(view_fg_color);
+  box-shadow: 0 0 0 1px themecolor(borders);
+  margin-top: 1px;
 
-  &:dir(rtl) { // axes left border and border radius
-    border-left-style: none;
-    border-top-left-radius: 0;
+  &:dir(rtl) {
+    border-top-right-radius: 6px;
+    margin-right: 1px;
   }
 
-  &:dir(ltr) { // axes right border and border radius
-    border-right-style: none;
-    border-top-right-radius: 0;
+  &:dir(ltr) {
+    border-top-left-radius: 6px;
+    margin-left: 1px;
   }
 
   button {
@@ -81,33 +77,25 @@
   background: rgba(13, 54, 104, 0.96);
   color: #ffffff;
 
-  label:backdrop {
-    color: #ffffff;
-  }
-
   .error & {
     border-top: 2px solid #ed333b;
     background: rgba(104, 13, 13, 0.96);
   }
 }
 
-label.numeric {
-  font-feature-settings: "tnum";
-}
-
 box.tab:drop(active) {
-  outline: 1px solid $drop_target_color;
+  outline: 1px solid themecolor(accent_bg_color);
   outline-offset: -1px;
-  -gtk-outline-radius: 0;
 
   window:not(.tiled)
         :not(.tiled-top)
         :not(.tiled-bottom)
         :not(.tiled-left)
         :not(.tiled-right)
+        :not(.fullscreen)
         :not(.maximized) & {
-    -gtk-outline-bottom-left-radius: 8px;
-    -gtk-outline-bottom-right-radius: 8px;
+    border-bottom-left-radius: 12px;
+    border-bottom-right-radius: 12px;
   }
 }
 
@@ -131,23 +119,8 @@ box.tab:drop(active) {
 }
 
 @mixin flat_round_button() {
-  background: none;
-  border: none;
-  box-shadow: none;
-  -gtk-icon-shadow: none;
-  text-shadow: none;
   border-radius: 99px;
-  -gtk-outline-radius: 99px;
-  color: inherit;
   padding: 0;
-
-  &:hover {
-    background: gtkalpha(currentColor, .15);
-
-    &:active {
-      background: gtkalpha(black, .2);
-    }
-  }
 }
 
 .tab-switcher {
@@ -156,59 +129,32 @@ box.tab:drop(active) {
   list {
     padding-top: 48px;
     padding-bottom: 84px;
+    background-color: themecolor(popover_bg_color);
+    color: themecolor(popover_fg_color);
 
     row {
-      background: none;
-      border: none;
-      box-shadow: none;
-      padding: 0;
-      outline-color: transparent;
-      color: themecolor(theme_text_color);
-
-      .content {
-        min-height: 40px;
-        margin-left: 5px;
-        margin-right: 5px;
-        margin-bottom: 2px;
-        padding: 3px;
-        border-radius: 5px;
-        -gtk-outline-radius: 5px;
-        border: none;
-        box-shadow: none;
-        transition: 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
-
-        &:dir(ltr) { padding-left: 11px; }
-        &:dir(rtl) { padding-right: 11px; }
-      }
+      min-height: 40px;
+      padding: 3px;
 
-      &:selected .content {
-        background: gtkalpha(currentColor, .06);
-      }
-
-      &:hover .content {
-        background: gtkalpha(currentColor, .1);
-      }
-
-      &:active .content {
-        background: gtkalpha(black, .15);
-      }
+      &:dir(ltr) { padding-left: 11px; }
+      &:dir(rtl) { padding-right: 11px; }
 
       &.needs-attention {
-        // Blue 4 | Blue 1
-        color: if($variant == 'light', #1c71d8, #99c1f1);
+        color: themecolor(accent_color);
       }
 
       .close-btn,
       .indicator-btn {
-        @include flat_round_button();
         min-width: 36px;
         min-height: 36px;
+        border-radius: 99px;
+        padding: 0;
       }
     }
   }
 
   .collapse-button {
-    @include flat_round_button();
+    border-radius: 99px;
     padding: 6px 30px;
     margin: 6px;
   }
@@ -217,7 +163,6 @@ box.tab:drop(active) {
     min-width: 48px;
     min-height: 48px;
     border-radius: 100%;
-    -gtk-outline-radius: 100%;
     padding: 0;
     margin: 18px;
   }
@@ -225,56 +170,48 @@ box.tab:drop(active) {
 
 themeswitcher {
   $_light_bg: white;
-  $_light_fg: transparentize(black, .1);
-  $_dark_bg: #2e3436;
+  $_light_fg: transparentize(black, .2);
+  $_dark_bg: #1e1e1e;
   $_dark_fg: white;
-  $_selected_color: #3584e4;
+
+  padding: 6px;
 
   .check {
-    background: $_selected_color;
+    background: themecolor(accent_bg_color);
     color: white;
     padding: 2px;
     border-radius: 17px;
+    margin: 3px;
   }
 
   /* Adapted from 
https://gitlab.gnome.org/GNOME/gnome-text-editor/-/blob/bf8c0c249f06a0be69e65aed3b786ba02a9f999e/src/TextEditor.css#L51
 */
-  radiobutton {
-    -gtk-outline-radius: 100px;
+  checkbutton {
     outline-offset: 1px;
     transition: none;
 
     radio {
       -gtk-icon-source: none;
       background: none;
-      box-shadow: none;
       padding: 12px;
       min-height: 24px;
       min-width: 24px;
       border: none;
       outline-color: currentColor;
       transition: none;
-
-      @if $high_contrast {
-        @if $variant == 'light' {
-          box-shadow: inset 0 0 0 1px transparentize(black, .3);
-        } @else {
-          box-shadow: inset 0 0 0 1px transparentize(white, .7);
-        }
-      } @else {
-        @if $variant == 'light' {
-          box-shadow: inset 0 0 0 1px transparentize(black, .8);
-        } @else {
-          box-shadow: 0 0 0 1px transparentize(black, .3);
-        }
-      }
+      box-shadow: inset 0 0 0 1px themecolor(borders);
 
       &:checked {
-        box-shadow: inset 0 0 0 2px $_selected_color;
+        box-shadow: inset 0 0 0 2px themecolor(accent_bg_color);
       }
     }
 
     &.system radio {
-      color: $_light_fg;
+      @if $variant == 'dark' {
+        color: $_dark_fg;
+      } else {
+        color: $_light_fg;
+      }
+
       background: linear-gradient(-45deg, $_dark_bg 49.99%, $_light_bg 50.01%);
     }
 
diff --git a/src/styles/meson.build b/src/styles/meson.build
index 0b3fab6..0528c97 100644
--- a/src/styles/meson.build
+++ b/src/styles/meson.build
@@ -8,8 +8,6 @@ if sassc.found()
   styles = [
     'styles-light',
     'styles-dark',
-    'styles-hc',
-    'styles-hc-dark',
   ]
 
   style_deps = []
@@ -25,9 +23,6 @@ if sassc.found()
           sassc, sassc_opts, '@INPUT@', '@OUTPUT@',
         ],
         depend_files: files([
-         '_definitions.scss',
-         '_drawing.scss',
-         '_recoloring.scss',
          '_styles.scss',
         ]),
       ),
diff --git a/subprojects/adwaita.wrap b/subprojects/adwaita.wrap
new file mode 100644
index 0000000..c06fb07
--- /dev/null
+++ b/subprojects/adwaita.wrap
@@ -0,0 +1,4 @@
+[wrap-git]
+directory=adwaita
+url=https://gitlab.gnome.org/GNOME/libadwaita.git
+revision=origin/main


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