[epiphany/wip/exalm/tabs: 4/6] Port to HdyTabView/HdyTabBar




commit 42ecee6a4173ede6c630ec7efb50880a1ed430d5
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Wed Jan 27 18:04:52 2021 +0500

    Port to HdyTabView/HdyTabBar
    
    Replace the aging GtkNotebook-based tabs with a new implementation.

 src/ephy-action-bar.c                             |   26 +-
 src/ephy-action-bar.h                             |    3 -
 src/ephy-desktop-utils.c                          |    4 +-
 src/ephy-header-bar.c                             |    1 -
 src/ephy-location-controller.c                    |   35 +-
 src/ephy-notebook.c                               | 1189 ---------------------
 src/ephy-notebook.h                               |   62 --
 src/ephy-page-row.c                               |  187 ++--
 src/ephy-page-row.h                               |   12 +-
 src/ephy-pages-popover.c                          |  274 -----
 src/ephy-pages-popover.h                          |   41 -
 src/ephy-pages-view.c                             |  164 +--
 src/ephy-pages-view.h                             |    8 +-
 src/ephy-session.c                                |  179 ++--
 src/ephy-shell.c                                  |   28 +-
 src/ephy-suggestion-model.c                       |   15 +-
 src/ephy-tab-label.c                              |  461 --------
 src/ephy-tab-label.h                              |   41 -
 src/ephy-tab-view.c                               |  654 ++++++++++++
 src/ephy-tab-view.h                               |   82 ++
 src/ephy-window.c                                 |  598 ++++-------
 src/ephy-window.h                                 |    3 +-
 src/meson.build                                   |    4 +-
 src/resources/ephy-missing-favicon-symbolic.svg   |   32 -
 src/resources/epiphany.gresource.xml              |    2 -
 src/resources/gtk/page-row.ui                     |    7 +
 src/resources/gtk/tab-label.ui                    |   90 --
 src/resources/themes/_Adwaita-base.scss           |   33 -
 src/resources/themes/_Adwaita-colored-window.scss |  198 +++-
 src/resources/themes/elementary.scss              |    6 -
 src/webextension/api/tabs.c                       |   10 +-
 src/webextension/ephy-web-extension-manager.c     |   32 +-
 src/window-commands.c                             |  239 +----
 src/window-commands.h                             |   12 -
 tests/ephy-shell-test.c                           |   51 +-
 35 files changed, 1468 insertions(+), 3315 deletions(-)
---
diff --git a/src/ephy-action-bar.c b/src/ephy-action-bar.c
index 248ee6536..a8f26de80 100644
--- a/src/ephy-action-bar.c
+++ b/src/ephy-action-bar.c
@@ -21,9 +21,9 @@
 
 #include "ephy-action-bar.h"
 #include "ephy-pages-button.h"
-#include "ephy-pages-popover.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
+#include "ephy-tab-view.h"
 
 enum {
   PROP_0,
@@ -40,7 +40,6 @@ struct _EphyActionBar {
   EphyActionBarStart *action_bar_start;
   EphyActionBarEnd *action_bar_end;
   EphyPagesButton *pages_button;
-  GtkNotebook *notebook;
 
   EphyAdaptiveMode adaptive_mode;
   gboolean can_reveal;
@@ -61,15 +60,6 @@ sync_chromes_visibility (EphyActionBar *action_bar)
                                                  chrome & EPHY_WINDOW_CHROME_BOOKMARKS);
 }
 
-static void
-update_pages_button (EphyActionBar *action_bar)
-{
-  int n_pages;
-
-  n_pages = gtk_notebook_get_n_pages (action_bar->notebook);
-  ephy_pages_button_set_n_pages (action_bar->pages_button, n_pages);
-}
-
 static void
 update_revealer (EphyActionBar *action_bar)
 {
@@ -121,21 +111,19 @@ static void
 ephy_action_bar_constructed (GObject *object)
 {
   EphyActionBar *action_bar = EPHY_ACTION_BAR (object);
+  EphyTabView *view;
 
   G_OBJECT_CLASS (ephy_action_bar_parent_class)->constructed (object);
 
-  action_bar->notebook = GTK_NOTEBOOK (ephy_window_get_notebook (action_bar->window));
-  update_pages_button (action_bar);
+  view = ephy_window_get_tab_view (action_bar->window);
 
   g_signal_connect_object (action_bar->window, "notify::chrome",
                            G_CALLBACK (sync_chromes_visibility), action_bar,
                            G_CONNECT_SWAPPED);
-  g_signal_connect_object (action_bar->notebook, "page-added",
-                           G_CALLBACK (update_pages_button), action_bar,
-                           G_CONNECT_SWAPPED);
-  g_signal_connect_object (action_bar->notebook, "page-removed",
-                           G_CALLBACK (update_pages_button), action_bar,
-                           G_CONNECT_SWAPPED);
+
+  g_object_bind_property (view, "n-pages",
+                          action_bar->pages_button, "n-pages",
+                          G_BINDING_SYNC_CREATE);
 }
 
 static void
diff --git a/src/ephy-action-bar.h b/src/ephy-action-bar.h
index da94137af..390e92007 100644
--- a/src/ephy-action-bar.h
+++ b/src/ephy-action-bar.h
@@ -26,7 +26,6 @@
 #include "ephy-action-bar-end.h"
 #include "ephy-action-bar-start.h"
 #include "ephy-adaptive-mode.h"
-#include "ephy-notebook.h"
 #include "ephy-window.h"
 
 G_BEGIN_DECLS
@@ -38,8 +37,6 @@ G_DECLARE_FINAL_TYPE (EphyActionBar, ephy_action_bar, EPHY, ACTION_BAR, GtkRevea
 EphyActionBar      *ephy_action_bar_new                  (EphyWindow *window);
 EphyActionBarStart *ephy_action_bar_get_action_bar_start (EphyActionBar *action_bar);
 EphyActionBarEnd   *ephy_action_bar_get_action_bar_end   (EphyActionBar *action_bar);
-void                ephy_action_bar_set_notebook         (EphyActionBar *action_bar,
-                                                          EphyNotebook  *notebook);
 void                ephy_action_bar_set_adaptive_mode    (EphyActionBar    *action_bar,
                                                           EphyAdaptiveMode  adaptive_mode);
 
diff --git a/src/ephy-desktop-utils.c b/src/ephy-desktop-utils.c
index 008ed4d6c..6d5412aa5 100644
--- a/src/ephy-desktop-utils.c
+++ b/src/ephy-desktop-utils.c
@@ -61,10 +61,10 @@ ephy_get_fallback_favicon_name (const char      *uri,
 {
   if (uri) {
     if (g_str_has_prefix (uri, "ephy-about:overview") || g_str_has_prefix (uri, "about:overview"))
-      return "view-grid-symbolic";
+      return (type == EPHY_FAVICON_TYPE_SHOW_MISSING_PLACEHOLDER) ? "view-grid-symbolic" : NULL;
     else if (g_str_has_prefix (uri, "ephy-about:") || g_str_has_prefix (uri, "about:"))
       return "web-browser-symbolic";
   }
 
-  return type == EPHY_FAVICON_TYPE_SHOW_MISSING_PLACEHOLDER ? "ephy-missing-favicon-symbolic" : NULL;
+  return NULL;
 }
diff --git a/src/ephy-header-bar.c b/src/ephy-header-bar.c
index 15569a365..104374628 100644
--- a/src/ephy-header-bar.c
+++ b/src/ephy-header-bar.c
@@ -29,7 +29,6 @@
 #include "ephy-file-helpers.h"
 #include "ephy-flatpak-utils.h"
 #include "ephy-location-entry.h"
-#include "ephy-notebook.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
 #include "ephy-title-box.h"
diff --git a/src/ephy-location-controller.c b/src/ephy-location-controller.c
index 01175c445..f9eaa6b1d 100644
--- a/src/ephy-location-controller.c
+++ b/src/ephy-location-controller.c
@@ -147,15 +147,14 @@ entry_activate_cb (GtkEntry               *entry,
     return;
 
   if (g_str_has_prefix (content, "ephy-tab://")) {
-    GtkWidget *notebook = ephy_window_get_notebook (controller->window);
+    EphyTabView *tab_view = ephy_window_get_tab_view (controller->window);
     GtkWidget *tab;
     EphyWebView *webview;
-    gint current = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
     g_auto (GStrv) split = g_strsplit (content + strlen ("ephy-tab://"), "@", -1);
 
     g_assert (g_strv_length (split) == 2);
 
-    tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), current);
+    tab = ephy_tab_view_get_selected_page (tab_view);
     webview = ephy_embed_get_web_view (EPHY_EMBED (tab));
 
     if (atoi (split[1]) != 0) {
@@ -169,15 +168,15 @@ entry_activate_cb (GtkEntry               *entry,
       windows = gtk_application_get_windows (GTK_APPLICATION (application));
 
       window = g_list_nth_data (windows, atoi (split[1]));
-      notebook = ephy_window_get_notebook (window);
+      tab_view = ephy_window_get_tab_view (window);
 
       gtk_window_present (GTK_WINDOW (window));
     }
 
-    gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), atoi (split[0]));
+    ephy_tab_view_select_nth_page (tab_view, atoi (split[0]));
 
     if (ephy_web_view_is_overview (webview))
-      g_signal_emit_by_name (GTK_NOTEBOOK (notebook), "tab-close-request", tab);
+      ephy_tab_view_close (tab_view, tab);
 
     return;
   }
@@ -295,10 +294,7 @@ focus_out_event_cb (GtkWidget              *entry,
 }
 
 static void
-switch_page_cb (GtkNotebook            *notebook,
-                GtkWidget              *page,
-                guint                   page_num,
-                EphyLocationController *controller)
+notify_selected_index_cb (EphyLocationController *controller)
 {
   if (controller->sync_address_is_blocked == TRUE) {
     controller->sync_address_is_blocked = FALSE;
@@ -343,15 +339,17 @@ ephy_location_controller_constructed (GObject *object)
   EphyHistoryService *history_service;
   EphyBookmarksManager *bookmarks_manager;
   EphySuggestionModel *model;
-  GtkWidget *notebook, *widget, *reader_mode, *entry;
+  EphyTabView *tab_view;
+  GtkWidget *widget, *reader_mode, *entry;
 
   G_OBJECT_CLASS (ephy_location_controller_parent_class)->constructed (object);
 
-  notebook = ephy_window_get_notebook (controller->window);
+  tab_view = ephy_window_get_tab_view (controller->window);
   widget = GTK_WIDGET (controller->title_widget);
 
-  g_signal_connect (notebook, "switch-page",
-                    G_CALLBACK (switch_page_cb), controller);
+  g_signal_connect_object (tab_view, "notify::selected-index",
+                           G_CALLBACK (notify_selected_index_cb), controller,
+                           G_CONNECT_SWAPPED);
 
   sync_address (controller, NULL, widget);
   g_signal_connect_object (controller, "notify::address",
@@ -446,14 +444,9 @@ static void
 ephy_location_controller_dispose (GObject *object)
 {
   EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
-  GtkWidget *notebook;
 
-  notebook = ephy_window_get_notebook (controller->window);
-
-  if (notebook == NULL ||
-      controller->title_widget == NULL) {
+  if (controller->title_widget == NULL)
     return;
-  }
 
   g_clear_object (&controller->longpress_gesture);
 
@@ -463,8 +456,6 @@ ephy_location_controller_dispose (GObject *object)
     g_signal_handlers_disconnect_matched (controller->title_widget, G_SIGNAL_MATCH_DATA,
                                           0, 0, NULL, NULL, controller);
   }
-  g_signal_handlers_disconnect_matched (notebook, G_SIGNAL_MATCH_DATA,
-                                        0, 0, NULL, NULL, controller);
   controller->title_widget = NULL;
 
   G_OBJECT_CLASS (ephy_location_controller_parent_class)->dispose (object);
diff --git a/src/ephy-page-row.c b/src/ephy-page-row.c
index b85438a0f..ccb239a68 100644
--- a/src/ephy-page-row.c
+++ b/src/ephy-page-row.c
@@ -22,67 +22,30 @@
 #include "config.h"
 
 #include "ephy-desktop-utils.h"
-#include "ephy-embed-utils.h"
+#include "ephy-embed.h"
 #include "ephy-page-row.h"
 #include "ephy-web-view.h"
 
-enum {
-  CLOSED,
-
-  LAST_SIGNAL
-};
-
 struct _EphyPageRow {
   GtkPopover parent_instance;
 
-  GtkBox *box;
   GtkImage *icon;
   GtkStack *icon_stack;
   GtkImage *speaker_icon;
   GtkSpinner *spinner;
   GtkLabel *title;
   GtkButton *close_button;
-};
 
-static guint signals[LAST_SIGNAL];
+  HdyTabPage *page;
+  EphyTabView *tab_view;
+};
 
 G_DEFINE_TYPE (EphyPageRow, ephy_page_row, GTK_TYPE_LIST_BOX_ROW)
 
-static void
-sync_load_status (EphyWebView *view,
-                  GParamSpec  *pspec,
-                  EphyPageRow *self)
-{
-  EphyEmbed *embed;
-
-  g_assert (EPHY_IS_WEB_VIEW (view));
-  g_assert (EPHY_IS_PAGE_ROW (self));
-
-  embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view);
-
-  g_assert (EPHY_IS_EMBED (embed));
-
-  if (ephy_web_view_is_loading (view) && !ephy_embed_has_load_pending (embed)) {
-    gtk_stack_set_visible_child (self->icon_stack, GTK_WIDGET (self->spinner));
-    gtk_spinner_start (GTK_SPINNER (self->spinner));
-  } else {
-    gtk_stack_set_visible_child (self->icon_stack, GTK_WIDGET (self->icon));
-    gtk_spinner_stop (GTK_SPINNER (self->spinner));
-  }
-}
-
-static void
-load_changed_cb (EphyWebView     *view,
-                 WebKitLoadEvent  load_event,
-                 EphyPageRow     *self)
-{
-  sync_load_status (view, NULL, self);
-}
-
 static void
 close_clicked_cb (EphyPageRow *self)
 {
-  g_signal_emit (self, signals[CLOSED], 0);
+  hdy_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
 }
 
 static gboolean
@@ -93,7 +56,7 @@ button_release_event (GtkWidget   *widget,
   GdkEventButton *button_event = (GdkEventButton *)event;
 
   if (button_event->button == GDK_BUTTON_MIDDLE) {
-    g_signal_emit (self, signals[CLOSED], 0);
+    hdy_tab_view_close_page (ephy_tab_view_get_tab_view (self->tab_view), self->page);
 
     return GDK_EVENT_STOP;
   }
@@ -106,15 +69,7 @@ ephy_page_row_class_init (EphyPageRowClass *klass)
 {
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
-  signals[CLOSED] =
-    g_signal_new ("closed",
-                  EPHY_TYPE_PAGE_ROW,
-                  G_SIGNAL_RUN_LAST,
-                  0, NULL, NULL, NULL,
-                  G_TYPE_NONE, 0);
-
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/epiphany/gtk/page-row.ui");
-  gtk_widget_class_bind_template_child (widget_class, EphyPageRow, box);
   gtk_widget_class_bind_template_child (widget_class, EphyPageRow, icon);
   gtk_widget_class_bind_template_child (widget_class, EphyPageRow, icon_stack);
   gtk_widget_class_bind_template_child (widget_class, EphyPageRow, speaker_icon);
@@ -131,84 +86,100 @@ ephy_page_row_init (EphyPageRow *self)
   gtk_widget_init_template (GTK_WIDGET (self));
 }
 
+static gboolean
+loading_to_visible_child (GBinding     *binding,
+                          const GValue *input,
+                          GValue       *output,
+                          EphyPageRow  *self)
+{
+  if (g_value_get_boolean (input))
+    g_value_set_object (output, self->spinner);
+  else
+    g_value_set_object (output, self->icon);
+
+  return TRUE;
+}
+
 static void
-sync_favicon (EphyWebView *view,
-              GParamSpec  *pspec,
-              EphyPageRow *self)
+update_icon_cb (EphyPageRow *self)
 {
-  GdkPixbuf *pixbuf = ephy_web_view_get_icon (view);
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (self->page));
+  EphyWebView *view = ephy_embed_get_web_view (embed);
+  GIcon *icon = G_ICON (ephy_web_view_get_icon (view));
+  const char *uri, *favicon_name;
+  HdyTabView *tab_view;
 
-  if (pixbuf)
-    gtk_image_set_from_gicon (GTK_IMAGE (self->icon), G_ICON (pixbuf), GTK_ICON_SIZE_MENU);
-  else {
-    const char *favicon_name = ephy_get_fallback_favicon_name (ephy_web_view_get_display_address (view), 
EPHY_FAVICON_TYPE_SHOW_MISSING_PLACEHOLDER);
+  if (icon) {
+    gtk_image_set_from_gicon (self->icon, icon, GTK_ICON_SIZE_MENU);
 
-    gtk_image_set_from_icon_name (GTK_IMAGE (self->icon), favicon_name, GTK_ICON_SIZE_MENU);
+    return;
   }
+
+  uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view));
+  favicon_name = ephy_get_fallback_favicon_name (uri, EPHY_FAVICON_TYPE_SHOW_MISSING_PLACEHOLDER);
+
+  if (favicon_name) {
+    g_autoptr (GIcon) fallback_icon = g_themed_icon_new (favicon_name);
+
+    gtk_image_set_from_gicon (self->icon, fallback_icon, GTK_ICON_SIZE_MENU);
+
+    return;
+  }
+
+  tab_view = ephy_tab_view_get_tab_view (self->tab_view);
+
+  gtk_image_set_from_gicon (self->icon, hdy_tab_view_get_default_icon (tab_view), GTK_ICON_SIZE_MENU);
 }
 
 EphyPageRow *
-ephy_page_row_new (EphyNotebook *notebook,
-                   gint          position)
+ephy_page_row_new (EphyTabView *tab_view,
+                   HdyTabPage  *page)
 {
   EphyPageRow *self;
-  GtkWidget *embed;
-  GtkWidget *tab_label;
+  GtkWidget *embed = hdy_tab_page_get_child (page);
   EphyWebView *view;
 
-  g_assert (notebook != NULL);
-  g_assert (position >= 0);
-
-  self = g_object_new (EPHY_TYPE_PAGE_ROW, NULL);
-
-  embed = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), position);
-
+  g_assert (HDY_IS_TAB_PAGE (page));
   g_assert (EPHY_IS_EMBED (embed));
 
-  tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), embed);
   view = ephy_embed_get_web_view (EPHY_EMBED (embed));
 
-  sync_favicon (view, NULL, self);
-  g_signal_connect_object (view, "notify::icon", G_CALLBACK (sync_favicon), self, 0);
-  g_object_bind_property (embed, "title", self->title, "label", G_BINDING_SYNC_CREATE);
-  g_object_bind_property (embed, "title", self->title, "tooltip-text", G_BINDING_SYNC_CREATE);
-  g_object_bind_property (view, "is-playing-audio", self->speaker_icon, "visible", G_BINDING_SYNC_CREATE);
-  g_object_bind_property (tab_label, "pinned", self->close_button, "visible", G_BINDING_SYNC_CREATE | 
G_BINDING_INVERT_BOOLEAN);
-  sync_load_status (view, NULL, self);
-  g_signal_connect_object (view, "load-changed",
-                           G_CALLBACK (load_changed_cb), self, 0);
+  self = g_object_new (EPHY_TYPE_PAGE_ROW, NULL);
+  self->tab_view = tab_view;
+  self->page = page;
+
+  g_object_bind_property (page, "title",
+                          self->title, "label",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (page, "indicator-icon",
+                          self->speaker_icon, "gicon",
+                          G_BINDING_SYNC_CREATE);
+  g_object_bind_property (page, "pinned",
+                          self->close_button, "visible",
+                          G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+  g_object_bind_property_full (page, "loading",
+                               self->icon_stack, "visible-child",
+                               G_BINDING_SYNC_CREATE,
+                               (GBindingTransformFunc)loading_to_visible_child,
+                               NULL,
+                               self, NULL);
+
+  g_signal_connect_object (view, "notify::icon",
+                           G_CALLBACK (update_icon_cb), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::uri",
+                           G_CALLBACK (update_icon_cb), self,
+                           G_CONNECT_SWAPPED);
+
+  update_icon_cb (self);
 
   return self;
 }
 
-void
-ephy_page_row_set_adaptive_mode (EphyPageRow      *self,
-                                 EphyAdaptiveMode  adaptive_mode)
+HdyTabPage *
+ephy_page_row_get_page (EphyPageRow *self)
 {
-  GtkStyleContext *context;
-
   g_assert (EPHY_IS_PAGE_ROW (self));
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (self));
-
-  switch (adaptive_mode) {
-    case EPHY_ADAPTIVE_MODE_NORMAL:
-      gtk_widget_set_size_request (GTK_WIDGET (self->box), -1, -1);
-      gtk_widget_set_margin_end (GTK_WIDGET (self->box), 0);
-      gtk_widget_set_margin_start (GTK_WIDGET (self->box), 4);
-      gtk_box_set_spacing (self->box, 0);
-
-      gtk_style_context_remove_class (context, "narrow");
-
-      break;
-    case EPHY_ADAPTIVE_MODE_NARROW:
-      gtk_widget_set_size_request (GTK_WIDGET (self->box), -1, 50);
-      gtk_widget_set_margin_end (GTK_WIDGET (self->box), 4);
-      gtk_widget_set_margin_start (GTK_WIDGET (self->box), 8);
-      gtk_box_set_spacing (self->box, 4);
-
-      gtk_style_context_add_class (context, "narrow");
-
-      break;
-  }
+  return self->page;
 }
diff --git a/src/ephy-page-row.h b/src/ephy-page-row.h
index 337a7ba92..6369359d7 100644
--- a/src/ephy-page-row.h
+++ b/src/ephy-page-row.h
@@ -21,9 +21,8 @@
 
 #pragma once
 
-#include <gtk/gtk.h>
-#include "ephy-adaptive-mode.h"
-#include "ephy-notebook.h"
+#include <handy.h>
+#include "ephy-tab-view.h"
 
 G_BEGIN_DECLS
 
@@ -31,10 +30,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (EphyPageRow, ephy_page_row, EPHY, PAGE_ROW, GtkListBoxRow)
 
-EphyPageRow *ephy_page_row_new (EphyNotebook *notebook,
-                                int           position);
+EphyPageRow *ephy_page_row_new (EphyTabView *view,
+                                HdyTabPage  *page);
 
-void ephy_page_row_set_adaptive_mode (EphyPageRow      *self,
-                                      EphyAdaptiveMode  adaptive_mode);
+HdyTabPage *ephy_page_row_get_page (EphyPageRow *self);
 
 G_END_DECLS
diff --git a/src/ephy-pages-view.c b/src/ephy-pages-view.c
index 94a7ad7d3..87a334b84 100644
--- a/src/ephy-pages-view.c
+++ b/src/ephy-pages-view.c
@@ -24,7 +24,6 @@
 
 #include "ephy-pages-view.h"
 
-#include "ephy-notebook.h"
 #include "ephy-page-row.h"
 #include "ephy-window.h"
 
@@ -33,126 +32,75 @@ struct _EphyPagesView {
 
   GtkListBox *list_box;
 
-  GListStore *list_store;
-  EphyNotebook *notebook;
+  GListModel *model;
+  EphyTabView *tab_view;
 };
 
 G_DEFINE_TYPE (EphyPagesView, ephy_pages_view, GTK_TYPE_BOX)
 
 static void
-drop_notebook (EphyPagesView *self)
+drop_tab_view (EphyPagesView *self)
 {
-  self->notebook = NULL;
-  g_list_store_remove_all (self->list_store);
+  self->tab_view = NULL;
 }
 
 static void
-release_notebook (EphyPagesView *self)
+release_tab_view (EphyPagesView *self)
 {
-  if (self->notebook) {
-    g_object_weak_unref (G_OBJECT (self->notebook), (GWeakNotify)drop_notebook, self);
-    drop_notebook (self);
+  if (self->tab_view) {
+    g_object_weak_unref (G_OBJECT (self->tab_view), (GWeakNotify)drop_tab_view, self);
+    drop_tab_view (self);
   }
 }
 
-static GtkWidget *
-create_row (gpointer item,
-            gpointer user_data)
-{
-  return GTK_WIDGET (g_object_ref (G_OBJECT (item)));
-}
-
 static void
 row_activated_cb (EphyPagesView *self,
-                  GtkListBoxRow *row)
+                  EphyPageRow   *row)
 {
-  gint new_page;
   EphyWindow *window;
   GApplication *application;
+  HdyTabPage *page;
 
   g_assert (EPHY_IS_PAGES_VIEW (self));
-  g_assert (!row || GTK_IS_LIST_BOX_ROW (row));
+  g_assert (EPHY_IS_PAGE_ROW (row));
 
   application = g_application_get_default ();
   window = EPHY_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (application)));
+  page = ephy_page_row_get_page (EPHY_PAGE_ROW (row));
 
-  if (!row)
-    return;
-
-  new_page = gtk_list_box_row_get_index (row);
-
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (self->notebook), new_page);
+  hdy_tab_view_set_selected_page (ephy_tab_view_get_tab_view (self->tab_view), page);
   ephy_window_close_pages_view (window);
 }
 
-static void
-row_closed_cb (EphyPagesView *self,
-               EphyPageRow   *row)
+static GtkWidget *
+create_row (HdyTabPage    *page,
+            EphyPagesView *self)
 {
-  GtkWindow *window;
-  GtkWidget *embed;
-  EphyEmbedShell *shell;
+  EphyPageRow *row = ephy_page_row_new (self->tab_view, page);
 
-  g_assert (EPHY_IS_PAGES_VIEW (self));
-  g_assert (EPHY_IS_PAGE_ROW (row));
-
-  shell = ephy_embed_shell_get_default ();
-  window = gtk_application_get_active_window (GTK_APPLICATION (shell));
+  gtk_widget_show (GTK_WIDGET (row));
 
-  embed = gtk_notebook_get_nth_page (GTK_NOTEBOOK (self->notebook),
-                                     gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (row)));
-  g_signal_emit_by_name (self->notebook,
-                         "tab-close-request",
-                         embed, window);
+  return GTK_WIDGET (row);
 }
 
-
 static void
-current_page_changed (EphyPagesView *self)
+selected_page_changed_cb (HdyTabView    *tab_view,
+                          GParamSpec    *pspec,
+                          EphyPagesView *self)
 {
-  GtkListBoxRow *current_row, *new_row;
-  gint current_page;
+  HdyTabPage *page = hdy_tab_view_get_selected_page (tab_view);
+  gint position;
+  GtkListBoxRow *row;
 
-  g_assert (EPHY_IS_PAGES_VIEW (self));
+  if (!page) {
+    gtk_list_box_unselect_all (self->list_box);
 
-  current_row = gtk_list_box_get_selected_row (self->list_box);
-  current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (self->notebook));
-  if (current_row && gtk_list_box_row_get_index (current_row) == current_page)
     return;
-
-  new_row = gtk_list_box_get_row_at_index (self->list_box, current_page);
-  gtk_list_box_select_row (self->list_box, new_row);
-}
-
-static void
-items_changed_cb (EphyPagesView *self,
-                  gint           position,
-                  gint           removed,
-                  gint           added,
-                  GMenuModel    *menu_model)
-{
-  g_autofree EphyPageRow **items = g_new (EphyPageRow *, added);
-
-  for (int i = 0; i < added; i++) {
-    items[i] = ephy_page_row_new (self->notebook, position + i);
-    ephy_page_row_set_adaptive_mode (EPHY_PAGE_ROW (items[i]),
-                                     EPHY_ADAPTIVE_MODE_NARROW);
-    g_signal_connect_swapped (items[i], "closed", G_CALLBACK (row_closed_cb), self);
   }
 
-  g_list_store_splice (self->list_store, position, removed, (gpointer)items, added);
-
-  current_page_changed (self);
-}
-
-static void
-ephy_pages_view_finalize (GObject *object)
-{
-  EphyPagesView *self = EPHY_PAGES_VIEW (object);
-
-  g_object_unref (self->list_store);
-
-  G_OBJECT_CLASS (ephy_pages_view_parent_class)->finalize (object);
+  position = hdy_tab_view_get_page_position (tab_view, page);
+  row = gtk_list_box_get_row_at_index (self->list_box, position);
+  gtk_list_box_select_row (self->list_box, row);
 }
 
 static void
@@ -160,7 +108,7 @@ ephy_pages_view_dispose (GObject *object)
 {
   EphyPagesView *self = EPHY_PAGES_VIEW (object);
 
-  release_notebook (self);
+  release_tab_view (self);
 
   G_OBJECT_CLASS (ephy_pages_view_parent_class)->dispose (object);
 }
@@ -172,7 +120,6 @@ ephy_pages_view_class_init (EphyPagesViewClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   object_class->dispose = ephy_pages_view_dispose;
-  object_class->finalize = ephy_pages_view_finalize;
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/epiphany/gtk/pages-view.ui");
   gtk_widget_class_bind_template_child (widget_class, EphyPagesView, list_box);
@@ -206,14 +153,6 @@ ephy_pages_view_init (EphyPagesView *self)
   gtk_widget_init_template (GTK_WIDGET (self));
 
   gtk_list_box_set_header_func (self->list_box, separator_header, NULL, NULL);
-
-  self->list_store = g_list_store_new (EPHY_TYPE_PAGE_ROW);
-
-  gtk_list_box_bind_model (self->list_box,
-                           G_LIST_MODEL (self->list_store),
-                           create_row,
-                           NULL,
-                           NULL);
 }
 
 EphyPagesView *
@@ -222,39 +161,40 @@ ephy_pages_view_new (void)
   return g_object_new (EPHY_TYPE_PAGES_VIEW, NULL);
 }
 
-EphyNotebook *
-ephy_pages_view_get_notebook (EphyPagesView *self)
+EphyTabView *
+ephy_pages_view_get_tab_view (EphyPagesView *self)
 {
   g_assert (EPHY_IS_PAGES_VIEW (self));
 
-  return self->notebook;
+  return self->tab_view;
 }
 
 void
-ephy_pages_view_set_notebook (EphyPagesView *self,
-                              EphyNotebook  *notebook)
+ephy_pages_view_set_tab_view (EphyPagesView *self,
+                              EphyTabView   *tab_view)
 {
-  GMenu *pages_menu;
-
   g_assert (EPHY_IS_PAGES_VIEW (self));
 
-  if (self->notebook)
-    release_notebook (self);
+  if (self->tab_view)
+    release_tab_view (self);
 
-  if (!notebook)
+  if (!tab_view)
     return;
 
-  g_object_weak_ref (G_OBJECT (notebook), (GWeakNotify)drop_notebook, self);
-  self->notebook = notebook;
-  pages_menu = ephy_notebook_get_pages_menu (EPHY_NOTEBOOK (notebook));
+  g_object_weak_ref (G_OBJECT (tab_view), (GWeakNotify)drop_tab_view, self);
+  self->tab_view = tab_view;
 
-  items_changed_cb (self, 0, 0,
-                    g_menu_model_get_n_items (G_MENU_MODEL (pages_menu)),
-                    G_MENU_MODEL (pages_menu));
+  self->model = hdy_tab_view_get_pages (ephy_tab_view_get_tab_view (tab_view));
+
+  gtk_list_box_bind_model (self->list_box,
+                           self->model,
+                           (GtkListBoxCreateWidgetFunc)create_row,
+                           self,
+                           NULL);
 
-  g_signal_connect_object (pages_menu,
-                           "items-changed",
-                           G_CALLBACK (items_changed_cb),
+  g_signal_connect_object (ephy_tab_view_get_tab_view (tab_view),
+                           "notify::selected-page",
+                           G_CALLBACK (selected_page_changed_cb),
                            self,
-                           G_CONNECT_SWAPPED);
+                           0);
 }
diff --git a/src/ephy-pages-view.h b/src/ephy-pages-view.h
index db910d1b8..6c05b0a7a 100644
--- a/src/ephy-pages-view.h
+++ b/src/ephy-pages-view.h
@@ -23,7 +23,7 @@
 #include <gtk/gtk.h>
 
 #include "ephy-adaptive-mode.h"
-#include "ephy-notebook.h"
+#include "ephy-tab-view.h"
 
 G_BEGIN_DECLS
 
@@ -33,8 +33,8 @@ G_DECLARE_FINAL_TYPE (EphyPagesView, ephy_pages_view, EPHY, PAGES_VIEW, GtkBox)
 
 EphyPagesView *ephy_pages_view_new               (void);
 
-EphyNotebook  *ephy_pages_view_get_notebook      (EphyPagesView   *view);
-void           ephy_pages_view_set_notebook      (EphyPagesView   *view,
-                                                  EphyNotebook    *notebook);
+EphyTabView   *ephy_pages_view_get_tab_view      (EphyPagesView *view);
+void           ephy_pages_view_set_tab_view      (EphyPagesView *view,
+                                                  EphyTabView   *tab_view);
 
 G_END_DECLS
diff --git a/src/ephy-session.c b/src/ephy-session.c
index cd7f71c8f..b13cc0900 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -32,11 +32,12 @@
 #include "ephy-file-helpers.h"
 #include "ephy-gui.h"
 #include "ephy-link.h"
-#include "ephy-notebook.h"
+#include "ephy-tab-view.h"
 #include "ephy-prefs.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
 #include "ephy-string.h"
+#include "ephy-tab-view.h"
 #include "ephy-window.h"
 
 #include <glib/gi18n.h>
@@ -45,12 +46,12 @@
 #include <libxml/xmlwriter.h>
 
 typedef struct {
-  EphyNotebook *notebook;
+  EphyTabView *tab_view;
   gint ref_count;
-} NotebookTracker;
+} TabViewTracker;
 
 typedef struct {
-  NotebookTracker *notebook_tracker;
+  TabViewTracker *tab_view_tracker;
   int position;
   char *url;
   WebKitWebViewSessionState *state;
@@ -132,35 +133,35 @@ load_changed_cb (WebKitWebView   *view,
 }
 
 static void
-notebook_tracker_set_notebook (NotebookTracker *tracker,
-                               EphyNotebook    *notebook)
+tab_view_tracker_set_tab_view (TabViewTracker *tracker,
+                               EphyTabView    *tab_view)
 {
-  if (tracker->notebook == notebook) {
+  if (tracker->tab_view == tab_view) {
     return;
   }
 
-  if (tracker->notebook) {
-    g_object_remove_weak_pointer (G_OBJECT (tracker->notebook), (gpointer *)&tracker->notebook);
+  if (tracker->tab_view) {
+    g_object_remove_weak_pointer (G_OBJECT (tracker->tab_view), (gpointer *)&tracker->tab_view);
   }
-  tracker->notebook = notebook;
-  if (tracker->notebook) {
-    g_object_add_weak_pointer (G_OBJECT (tracker->notebook), (gpointer *)&tracker->notebook);
+  tracker->tab_view = tab_view;
+  if (tracker->tab_view) {
+    g_object_add_weak_pointer (G_OBJECT (tracker->tab_view), (gpointer *)&tracker->tab_view);
   }
 }
 
-static NotebookTracker *
-notebook_tracker_new (EphyNotebook *notebook)
+static TabViewTracker *
+tab_view_tracker_new (EphyTabView *tab_view)
 {
-  NotebookTracker *tracker = g_new0 (NotebookTracker, 1);
+  TabViewTracker *tracker = g_new0 (TabViewTracker, 1);
 
   tracker->ref_count = 1;
-  notebook_tracker_set_notebook (tracker, notebook);
+  tab_view_tracker_set_tab_view (tracker, tab_view);
 
   return tracker;
 }
 
-static NotebookTracker *
-notebook_tracker_ref (NotebookTracker *tracker)
+static TabViewTracker *
+tab_view_tracker_ref (TabViewTracker *tracker)
 {
   g_atomic_int_inc (&tracker->ref_count);
 
@@ -168,57 +169,57 @@ notebook_tracker_ref (NotebookTracker *tracker)
 }
 
 static void
-notebook_tracker_unref (NotebookTracker *tracker)
+tab_view_tracker_unref (TabViewTracker *tracker)
 {
   if (!g_atomic_int_dec_and_test (&tracker->ref_count))
     return;
 
-  notebook_tracker_set_notebook (tracker, NULL);
+  tab_view_tracker_set_tab_view (tracker, NULL);
   g_free (tracker);
 }
 
-static EphyNotebook *
-closed_tab_get_notebook (ClosedTab *tab)
+static EphyTabView *
+closed_tab_get_tab_view (ClosedTab *tab)
 {
-  return tab->notebook_tracker->notebook;
+  return tab->tab_view_tracker->tab_view;
 }
 
 static int
-compare_func (ClosedTab    *iter,
-              EphyNotebook *notebook)
+compare_func (ClosedTab   *iter,
+              EphyTabView *tab_view)
 {
-  return GTK_NOTEBOOK (closed_tab_get_notebook (iter)) - GTK_NOTEBOOK (notebook);
+  return (gpointer)closed_tab_get_tab_view (iter) - (gpointer)tab_view;
 }
 
-static NotebookTracker *
-ephy_session_ref_or_create_notebook_tracker (EphySession  *session,
-                                             EphyNotebook *notebook)
+static TabViewTracker *
+ephy_session_ref_or_create_tab_view_tracker (EphySession *session,
+                                             EphyTabView *tab_view)
 {
-  GList *item = g_queue_find_custom (session->closed_tabs, notebook, (GCompareFunc)compare_func);
-  return item ? notebook_tracker_ref (((ClosedTab *)item->data)->notebook_tracker) : notebook_tracker_new 
(notebook);
+  GList *item = g_queue_find_custom (session->closed_tabs, tab_view, (GCompareFunc)compare_func);
+  return item ? tab_view_tracker_ref (((ClosedTab *)item->data)->tab_view_tracker) : tab_view_tracker_new 
(tab_view);
 }
 
 static void
 closed_tab_free (ClosedTab *tab)
 {
   g_free (tab->url);
-  notebook_tracker_unref (tab->notebook_tracker);
+  tab_view_tracker_unref (tab->tab_view_tracker);
   webkit_web_view_session_state_unref (tab->state);
 
   g_free (tab);
 }
 
 static ClosedTab *
-closed_tab_new (EphyWebView     *web_view,
-                int              position,
-                NotebookTracker *notebook_tracker)
+closed_tab_new (EphyWebView    *web_view,
+                int             position,
+                TabViewTracker *tab_view_tracker)
 {
   ClosedTab *tab = g_new0 (ClosedTab, 1);
 
   tab->url = g_strdup (ephy_web_view_get_address (web_view));
   tab->position = position;
   /* Takes the ownership of the tracker */
-  tab->notebook_tracker = notebook_tracker;
+  tab->tab_view_tracker = tab_view_tracker;
   tab->state = webkit_web_view_get_session_state (WEBKIT_WEB_VIEW (web_view));
 
   return tab;
@@ -233,7 +234,7 @@ ephy_session_undo_close_tab (EphySession *session)
   WebKitBackForwardListItem *item;
   ClosedTab *tab;
   EphyWindow *window;
-  EphyNotebook *notebook;
+  EphyTabView *tab_view;
   EphyNewTabFlags flags = EPHY_NEW_TAB_JUMP;
 
   g_assert (EPHY_IS_SESSION (session));
@@ -243,12 +244,12 @@ ephy_session_undo_close_tab (EphySession *session)
     return;
 
   LOG ("UNDO CLOSE TAB: %s", tab->url);
-  notebook = closed_tab_get_notebook (tab);
-  if (notebook) {
+  tab_view = closed_tab_get_tab_view (tab);
+  if (tab_view) {
     if (tab->position > 0) {
       /* Append in the n-th position. */
-      embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook),
-                                                     tab->position - 1));
+      embed = EPHY_EMBED (ephy_tab_view_get_nth_page (tab_view,
+                                                      tab->position - 1));
       flags |= EPHY_NEW_TAB_APPEND_AFTER;
     } else {
       /* Just prepend in the first position. */
@@ -256,7 +257,7 @@ ephy_session_undo_close_tab (EphySession *session)
       flags |= EPHY_NEW_TAB_FIRST;
     }
 
-    window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (notebook)));
+    window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab_view)));
     new_tab = ephy_shell_new_tab (ephy_shell_get_default (),
                                   window, embed,
                                   flags);
@@ -264,8 +265,8 @@ ephy_session_undo_close_tab (EphySession *session)
     window = ephy_window_new ();
     new_tab = ephy_shell_new_tab (ephy_shell_get_default (),
                                   window, NULL, flags);
-    notebook_tracker_set_notebook (tab->notebook_tracker,
-                                   EPHY_NOTEBOOK (ephy_window_get_notebook (window)));
+    tab_view_tracker_set_tab_view (tab->tab_view_tracker,
+                                   ephy_window_get_tab_view (window));
   }
 
   web_view = WEBKIT_WEB_VIEW (ephy_embed_get_web_view (new_tab));
@@ -288,10 +289,10 @@ ephy_session_undo_close_tab (EphySession *session)
 }
 
 static void
-ephy_session_tab_closed (EphySession  *session,
-                         EphyNotebook *notebook,
-                         EphyEmbed    *embed,
-                         gint          position)
+ephy_session_tab_closed (EphySession *session,
+                         EphyTabView *tab_view,
+                         EphyEmbed   *embed,
+                         gint         position)
 {
   EphyWebView *view;
   WebKitWebView *wk_view;
@@ -306,7 +307,7 @@ ephy_session_tab_closed (EphySession  *session,
   }
 
   tab = closed_tab_new (view, position,
-                        ephy_session_ref_or_create_notebook_tracker (session, notebook));
+                        ephy_session_ref_or_create_tab_view_tracker (session, tab_view));
   g_queue_push_head (session->closed_tabs, tab);
 
   if (g_queue_get_length (session->closed_tabs) == 1)
@@ -325,33 +326,38 @@ ephy_session_get_can_undo_tab_closed (EphySession *session)
 }
 
 static void
-notebook_page_added_cb (GtkWidget   *notebook,
-                        EphyEmbed   *embed,
-                        guint        position,
-                        EphySession *session)
+tab_view_page_attached_cb (HdyTabView  *tab_view,
+                           HdyTabPage  *page,
+                           guint        position,
+                           EphySession *session)
 {
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+
   g_signal_connect (ephy_embed_get_web_view (embed), "load-changed",
                     G_CALLBACK (load_changed_cb), session);
 }
 
 static void
-notebook_page_removed_cb (GtkWidget   *notebook,
-                          EphyEmbed   *embed,
-                          guint        position,
-                          EphySession *session)
+tab_view_page_detached_cb (HdyTabView  *tab_view,
+                           HdyTabPage  *page,
+                           gint         position,
+                           EphySession *session)
 {
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyTabView *ephy_tab_view = EPHY_TAB_VIEW (g_object_get_data (G_OBJECT (tab_view), "ephy-tab-view"));
+
   ephy_session_save (session);
 
   g_signal_handlers_disconnect_by_func
     (ephy_embed_get_web_view (embed), G_CALLBACK (load_changed_cb),
     session);
 
-  ephy_session_tab_closed (session, EPHY_NOTEBOOK (notebook), embed, position);
+  ephy_session_tab_closed (session, ephy_tab_view, embed, position);
 }
 
 static void
-notebook_page_reordered_cb (GtkWidget   *notebook,
-                            GtkWidget   *tab,
+tab_view_page_reordered_cb (HdyTabView  *tab_view,
+                            HdyTabPage  *page,
                             guint        position,
                             EphySession *session)
 {
@@ -359,10 +365,9 @@ notebook_page_reordered_cb (GtkWidget   *notebook,
 }
 
 static void
-notebook_switch_page_cb (GtkNotebook *notebook,
-                         GtkWidget   *page,
-                         guint        page_num,
-                         EphySession *session)
+tab_view_notify_selected_page_cb (HdyTabView  *tab_view,
+                                  GParamSpec  *pspec,
+                                  EphySession *session)
 {
   ephy_session_save (session);
 }
@@ -386,8 +391,8 @@ window_added_cb (GtkApplication *application,
                  GtkWindow      *window,
                  EphySession    *session)
 {
-  GtkWidget *notebook;
   EphyWindow *ephy_window;
+  HdyTabView *tab_view;
 
   ephy_session_save (session);
 
@@ -396,15 +401,15 @@ window_added_cb (GtkApplication *application,
 
   ephy_window = EPHY_WINDOW (window);
 
-  notebook = ephy_window_get_notebook (ephy_window);
-  g_signal_connect (notebook, "page-added",
-                    G_CALLBACK (notebook_page_added_cb), session);
-  g_signal_connect (notebook, "page-removed",
-                    G_CALLBACK (notebook_page_removed_cb), session);
-  g_signal_connect (notebook, "page-reordered",
-                    G_CALLBACK (notebook_page_reordered_cb), session);
-  g_signal_connect_after (notebook, "switch-page",
-                          G_CALLBACK (notebook_switch_page_cb), session);
+  tab_view = ephy_tab_view_get_tab_view (ephy_window_get_tab_view (ephy_window));
+  g_signal_connect (tab_view, "page-attached",
+                    G_CALLBACK (tab_view_page_attached_cb), session);
+  g_signal_connect (tab_view, "page-detached",
+                    G_CALLBACK (tab_view_page_detached_cb), session);
+  g_signal_connect (tab_view, "page-reordered",
+                    G_CALLBACK (tab_view_page_reordered_cb), session);
+  g_signal_connect_after (tab_view, "notify::selected-page",
+                          G_CALLBACK (tab_view_notify_selected_page_cb), session);
 
   /* Set unique identifier as role, so that on restore, the WM can
    * place the window on the right workspace
@@ -563,7 +568,7 @@ typedef struct {
 static SessionTab *
 session_tab_new (EphyEmbed   *embed,
                  EphySession *session,
-                 GtkNotebook *notebook)
+                 EphyTabView *tab_view)
 {
   SessionTab *session_tab;
   const char *address;
@@ -592,7 +597,7 @@ session_tab_new (EphyEmbed   *embed,
   session_tab->crashed = (error_page == EPHY_WEB_VIEW_ERROR_PAGE_CRASH ||
                           error_page == EPHY_WEB_VIEW_ERROR_PROCESS_CRASH);
   session_tab->state = webkit_web_view_get_session_state (WEBKIT_WEB_VIEW (web_view));
-  session_tab->pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed);
+  session_tab->pinned = ephy_tab_view_get_is_pinned (tab_view, GTK_WIDGET (embed));
 
   return session_tab;
 }
@@ -613,7 +618,7 @@ session_window_new (EphyWindow  *window,
 {
   SessionWindow *session_window;
   GList *tabs, *l;
-  GtkNotebook *notebook;
+  EphyTabView *tab_view;
 
   tabs = ephy_embed_container_get_children (EPHY_EMBED_CONTAINER (window));
   /* Do not save an empty EphyWindow.
@@ -626,18 +631,18 @@ session_window_new (EphyWindow  *window,
   session_window = g_new0 (SessionWindow, 1);
   get_window_geometry (window, session_window);
   session_window->role = g_strdup (gtk_window_get_role (GTK_WINDOW (window)));
-  notebook = GTK_NOTEBOOK (ephy_window_get_notebook (window));
+  tab_view = ephy_window_get_tab_view (window);
 
   for (l = tabs; l != NULL; l = l->next) {
     SessionTab *tab;
 
-    tab = session_tab_new (EPHY_EMBED (l->data), session, notebook);
+    tab = session_tab_new (EPHY_EMBED (l->data), session, tab_view);
     session_window->tabs = g_list_prepend (session_window->tabs, tab);
   }
   g_list_free (tabs);
   session_window->tabs = g_list_reverse (session_window->tabs);
 
-  session_window->active_tab = gtk_notebook_get_current_page (notebook);
+  session_window->active_tab = ephy_tab_view_get_selected_index (tab_view);
 
   return session_window;
 }
@@ -1184,7 +1189,7 @@ session_parse_embed (SessionParserContext  *context,
                      const gchar          **names,
                      const gchar          **values)
 {
-  GtkWidget *notebook;
+  HdyTabView *tab_view;
   const char *url = NULL;
   const char *title = NULL;
   const char *history = NULL;
@@ -1201,7 +1206,7 @@ session_parse_embed (SessionParserContext  *context,
     return;
   }
 
-  notebook = ephy_window_get_notebook (context->window);
+  tab_view = ephy_tab_view_get_tab_view (ephy_window_get_tab_view (context->window));
 
   for (i = 0; names[i]; i++) {
     if (strcmp (names[i], "url") == 0) {
@@ -1251,7 +1256,9 @@ session_parse_embed (SessionParserContext  *context,
                                      context->window, NULL, flags,
                                      0);
 
-    ephy_notebook_tab_set_pinned (EPHY_NOTEBOOK (notebook), GTK_WIDGET (embed), is_pin);
+    hdy_tab_view_set_page_pinned (tab_view,
+                                  hdy_tab_view_get_page (tab_view, GTK_WIDGET (embed)),
+                                  is_pin);
 
     web_view = ephy_embed_get_web_view (embed);
     if (history) {
@@ -1327,7 +1334,7 @@ session_end_element (GMarkupParseContext  *ctx,
   SessionParserContext *context = (SessionParserContext *)user_data;
 
   if (strcmp (element_name, "window") == 0) {
-    GtkWidget *notebook;
+    EphyTabView *tab_view;
     EphyEmbedShell *shell = ephy_embed_shell_get_default ();
 
     if (!context->window) {
@@ -1337,8 +1344,8 @@ session_end_element (GMarkupParseContext  *ctx,
       return;
     }
 
-    notebook = ephy_window_get_notebook (context->window);
-    gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), context->active_tab);
+    tab_view = ephy_window_get_tab_view (context->window);
+    ephy_tab_view_select_nth_page (tab_view, context->active_tab);
 
     if (ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) != EPHY_EMBED_SHELL_MODE_TEST) {
       EphyEmbed *active_child;
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 18ac9ff84..ae19357d9 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -935,23 +935,22 @@ webkit_notification_clicked_cb (WebKitNotification *notification,
 
   for (guint win_idx = 0; win_idx < g_list_length (windows); win_idx++) {
     EphyWindow *window;
-    GtkWidget *notebook;
+    EphyTabView *tab_view;
     int n_pages;
 
     window = EPHY_WINDOW (g_list_nth_data (windows, win_idx));
-
-    notebook = ephy_window_get_notebook (window);
-    n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
+    tab_view = ephy_window_get_tab_view (window);
+    n_pages = ephy_tab_view_get_n_pages (tab_view);
 
     for (int i = 0; i < n_pages; i++) {
       EphyEmbed *embed;
       WebKitWebView *webview;
 
-      embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
+      embed = EPHY_EMBED (ephy_tab_view_get_nth_page (tab_view, i));
       webview = WEBKIT_WEB_VIEW (ephy_embed_get_web_view (embed));
 
       if (webview == notification_webview) {
-        gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
+        ephy_tab_view_select_page (tab_view, GTK_WIDGET (embed));
         gtk_window_present (GTK_WINDOW (window));
         return;
       }
@@ -1556,16 +1555,16 @@ ephy_shell_get_web_view (EphyShell *shell,
 {
   GList *windows;
   GtkWindow *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
 
   windows = gtk_application_get_windows (GTK_APPLICATION (shell));
 
   for (GList *list = windows; list && list->data; list = list->next) {
     window = GTK_WINDOW (list->data);
-    notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+    tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
 
-    for (int i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); i++) {
-      GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+    for (int i = 0; i < ephy_tab_view_get_n_pages (tab_view); i++) {
+      GtkWidget *page = ephy_tab_view_get_nth_page (tab_view, i);
       EphyWebView *web_view = ephy_embed_get_web_view (EPHY_EMBED (page));
 
       if (ephy_web_view_get_uid (web_view) == id)
@@ -1580,18 +1579,15 @@ EphyWebView *
 ephy_shell_get_active_web_view (EphyShell *shell)
 {
   GtkWindow *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
   GtkWidget *page;
-  gint page_num;
 
   window = gtk_application_get_active_window (GTK_APPLICATION (shell));
   if (!window)
     return NULL;
 
-  notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
-
-  page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
-  page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), page_num);
+  tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
+  page = ephy_tab_view_get_selected_page (tab_view);
 
   return ephy_embed_get_web_view (EPHY_EMBED (page));
 }
diff --git a/src/ephy-suggestion-model.c b/src/ephy-suggestion-model.c
index 1c5fb6793..d8948e7f6 100644
--- a/src/ephy-suggestion-model.c
+++ b/src/ephy-suggestion-model.c
@@ -407,10 +407,9 @@ add_tabs (EphySuggestionModel *self,
   GApplication *application;
   EphyEmbedShell *shell;
   EphyWindow *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
   GList *windows;
-  gint n_pages;
-  gint current;
+  gint n_pages, selected;
   guint added = 0;
 
   shell = ephy_embed_shell_get_default ();
@@ -420,9 +419,9 @@ add_tabs (EphySuggestionModel *self,
   for (guint win_idx = 0; win_idx < g_list_length (windows); win_idx++) {
     window = EPHY_WINDOW (g_list_nth_data (windows, win_idx));
 
-    notebook = ephy_window_get_notebook (window);
-    n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
-    current = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
+    tab_view = ephy_window_get_tab_view (window);
+    n_pages = ephy_tab_view_get_n_pages (tab_view);
+    selected = ephy_tab_view_get_selected_index (tab_view);
 
     for (int i = 0; i < n_pages; i++) {
       EphyEmbed *embed;
@@ -438,10 +437,10 @@ add_tabs (EphySuggestionModel *self,
       g_autofree gchar *display_address_casefold = NULL;
       g_autofree gchar *query_casefold = NULL;
 
-      if (win_idx == 0 && i == current)
+      if (win_idx == 0 && i == selected)
         continue;
 
-      embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
+      embed = EPHY_EMBED (ephy_tab_view_get_nth_page (tab_view, i));
       webview = ephy_embed_get_web_view (embed);
       display_address = ephy_web_view_get_display_address (webview);
       url = ephy_web_view_get_address (webview);
diff --git a/src/ephy-tab-view.c b/src/ephy-tab-view.c
new file mode 100644
index 000000000..6a4bf0c27
--- /dev/null
+++ b/src/ephy-tab-view.c
@@ -0,0 +1,654 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2020 Alexander Mikhaylenko <exalm7659 gmail com>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ephy-tab-view.h"
+
+#include "ephy-desktop-utils.h"
+#include "ephy-dnd.h"
+#include "ephy-embed-utils.h"
+#include "ephy-link.h"
+#include "ephy-settings.h"
+#include "ephy-shell.h"
+
+#define INSANE_NUMBER_OF_URLS 20
+
+struct _EphyTabView {
+  GtkBin parent_instance;
+
+  HdyTabView *tab_view;
+  HdyTabBar *tab_bar;
+  HdyTabPage *current_page;
+};
+
+G_DEFINE_TYPE (EphyTabView, ephy_tab_view, GTK_TYPE_BIN)
+
+enum {
+  PROP_0,
+  PROP_N_PAGES,
+  PROP_SELECTED_INDEX,
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+notify_n_pages_cb (EphyTabView *self)
+{
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_PAGES]);
+}
+
+static void
+notify_selected_page_cb (EphyTabView *self)
+{
+  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+
+  if (page)
+    hdy_tab_page_set_needs_attention (page, FALSE);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_INDEX]);
+}
+
+static void
+indicator_activated_cb (EphyTabView *self,
+                        HdyTabPage  *page)
+{
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyWebView *view = ephy_embed_get_web_view (embed);
+  gboolean muted = webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view));
+
+  webkit_web_view_set_is_muted (WEBKIT_WEB_VIEW (view), !muted);
+}
+
+static void
+setup_menu_cb (EphyTabView *self,
+               HdyTabPage  *page)
+{
+  self->current_page = page;
+}
+
+static HdyTabPage *
+get_current_page (EphyTabView *self)
+{
+  if (self->current_page)
+    return self->current_page;
+
+  return hdy_tab_view_get_selected_page (self->tab_view);
+}
+
+static void
+ephy_tab_view_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  EphyTabView *self = EPHY_TAB_VIEW (object);
+
+  switch (prop_id) {
+    case PROP_N_PAGES:
+      g_value_set_int (value, ephy_tab_view_get_n_pages (self));
+      break;
+
+    case PROP_SELECTED_INDEX:
+      g_value_set_int (value, ephy_tab_view_get_selected_index (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_tab_view_class_init (EphyTabViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = ephy_tab_view_get_property;
+
+  properties[PROP_N_PAGES] =
+    g_param_spec_int ("n-pages",
+                      "Number of pages",
+                      "The number of pages in the tab view",
+                      0,
+                      G_MAXINT,
+                      0,
+                      (G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_EXPLICIT_NOTIFY));
+
+  properties[PROP_SELECTED_INDEX] =
+    g_param_spec_int ("selected-index",
+                      "Selected index",
+                      "The index of the currently selected page",
+                      0,
+                      G_MAXINT,
+                      0,
+                      (G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_EXPLICIT_NOTIFY));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ephy_tab_view_init (EphyTabView *self)
+{
+  self->tab_view = HDY_TAB_VIEW (hdy_tab_view_new ());
+  gtk_widget_show (GTK_WIDGET (self->tab_view));
+
+  g_object_set_data (G_OBJECT (self->tab_view), "ephy-tab-view", self);
+
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->tab_view));
+
+  g_signal_connect_object (self->tab_view,
+                           "notify::n-pages",
+                           G_CALLBACK (notify_n_pages_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->tab_view,
+                           "notify::selected-page",
+                           G_CALLBACK (notify_selected_page_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->tab_view,
+                           "setup-menu",
+                           G_CALLBACK (setup_menu_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->tab_view,
+                           "indicator-activated",
+                           G_CALLBACK (indicator_activated_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+EphyTabView *
+ephy_tab_view_new (void)
+{
+  return g_object_new (EPHY_TYPE_TAB_VIEW, NULL);
+}
+
+void
+ephy_tab_view_next (EphyTabView *self)
+{
+  hdy_tab_view_select_next_page (self->tab_view);
+}
+
+void
+ephy_tab_view_pin (EphyTabView *self)
+{
+  hdy_tab_view_set_page_pinned (self->tab_view, get_current_page (self), TRUE);
+}
+
+void
+ephy_tab_view_unpin (EphyTabView *self)
+{
+  hdy_tab_view_set_page_pinned (self->tab_view, get_current_page (self), FALSE);
+}
+
+void
+ephy_tab_view_close (EphyTabView *self,
+                     GtkWidget   *widget)
+{
+  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+
+  hdy_tab_view_close_page (self->tab_view, page);
+}
+
+void
+ephy_tab_view_close_selected (EphyTabView *self)
+{
+  hdy_tab_view_close_page (self->tab_view, get_current_page (self));
+}
+
+void
+ephy_tab_view_close_left (EphyTabView *self)
+{
+  hdy_tab_view_close_pages_before (self->tab_view, get_current_page (self));
+}
+
+void
+ephy_tab_view_close_right (EphyTabView *self)
+{
+  hdy_tab_view_close_pages_after (self->tab_view, get_current_page (self));
+}
+
+void
+ephy_tab_view_close_other (EphyTabView *self)
+{
+  hdy_tab_view_close_other_pages (self->tab_view, get_current_page (self));
+}
+
+void
+ephy_tab_view_foreach (EphyTabView *self,
+                       GtkCallback  callback,
+                       gpointer     user_data)
+{
+  gint i, n;
+
+  n = hdy_tab_view_get_n_pages (self->tab_view);
+
+  for (i = 0; i < n; i++) {
+    HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, i);
+
+    callback (hdy_tab_page_get_child (page), user_data);
+  }
+}
+
+gint
+ephy_tab_view_get_n_pages (EphyTabView *self)
+{
+  return hdy_tab_view_get_n_pages (self->tab_view);
+}
+
+gint
+ephy_tab_view_get_selected_index (EphyTabView *self)
+{
+  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+
+  if (!page)
+    return -1;
+
+  return hdy_tab_view_get_page_position (self->tab_view, page);
+}
+
+gint
+ephy_tab_view_get_page_index (EphyTabView *self,
+                              GtkWidget   *widget)
+{
+  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+
+  return hdy_tab_view_get_page_position (self->tab_view, page);
+}
+
+GtkWidget *
+ephy_tab_view_get_nth_page (EphyTabView *self,
+                            gint         index)
+{
+  HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, index);
+
+  return hdy_tab_page_get_child (page);
+}
+
+void
+ephy_tab_view_select_nth_page (EphyTabView *self,
+                               gint         index)
+{
+  HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, index);
+
+  hdy_tab_view_set_selected_page (self->tab_view, page);
+}
+
+gboolean
+ephy_tab_view_select_page (EphyTabView *self,
+                           GtkWidget   *widget)
+{
+  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+
+  if (page)
+    hdy_tab_view_set_selected_page (self->tab_view, page);
+
+  return page != NULL;
+}
+
+GtkWidget *
+ephy_tab_view_get_selected_page (EphyTabView *self)
+{
+  HdyTabPage *page = hdy_tab_view_get_selected_page (self->tab_view);
+
+  return hdy_tab_page_get_child (page);
+}
+
+HdyTabView *
+ephy_tab_view_get_tab_view (EphyTabView *self)
+{
+  return self->tab_view;
+}
+
+GList *
+ephy_tab_view_get_pages (EphyTabView *self)
+{
+  GList *list = NULL;
+  gint i, n;
+
+  n = hdy_tab_view_get_n_pages (self->tab_view);
+
+  for (i = 0; i < n; i++) {
+    HdyTabPage *page = hdy_tab_view_get_nth_page (self->tab_view, i);
+    GtkWidget *content = hdy_tab_page_get_child (page);
+
+    list = g_list_prepend (list, content);
+  }
+
+  return g_list_reverse (list);
+}
+
+gboolean
+ephy_tab_view_get_is_pinned (EphyTabView *self,
+                             GtkWidget   *widget)
+{
+  HdyTabPage *page = hdy_tab_view_get_page (self->tab_view, widget);
+
+  return hdy_tab_page_get_pinned (page);
+}
+
+static void
+update_title_cb (HdyTabPage *page)
+{
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyWebView *view = ephy_embed_get_web_view (embed);
+  const gchar *title = ephy_embed_get_title (embed);
+  const gchar *address;
+
+  if (!ephy_embed_has_load_pending (embed) &&
+      !hdy_tab_page_get_selected (page) &&
+      hdy_tab_page_get_pinned (page))
+    hdy_tab_page_set_needs_attention (page, TRUE);
+
+  if (title && strlen (title)) {
+    hdy_tab_page_set_title (page, title);
+
+    return;
+  }
+
+  address = ephy_web_view_get_display_address (view);
+
+  if (ephy_web_view_is_loading (view) &&
+      !ephy_embed_utils_is_no_show_address (address))
+    hdy_tab_page_set_title (page, address);
+}
+
+static void
+update_icon_cb (HdyTabPage *page)
+{
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyWebView *view = ephy_embed_get_web_view (embed);
+  g_autoptr (GIcon) icon = G_ICON (ephy_web_view_get_icon (view));
+  const char *uri, *favicon_name;
+
+  if (icon) {
+    hdy_tab_page_set_icon (page, g_steal_pointer (&icon));
+
+    return;
+  }
+
+  uri = webkit_web_view_get_uri (WEBKIT_WEB_VIEW (view));
+  favicon_name = ephy_get_fallback_favicon_name (uri, EPHY_FAVICON_TYPE_NO_MISSING_PLACEHOLDER);
+
+  if (favicon_name)
+    icon = g_themed_icon_new (favicon_name);
+
+  hdy_tab_page_set_icon (page, icon);
+}
+
+static void
+update_indicator_cb (HdyTabPage *page)
+{
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  EphyWebView *view = ephy_embed_get_web_view (embed);
+  GIcon *icon = NULL;
+
+  if (webkit_web_view_is_playing_audio (WEBKIT_WEB_VIEW (view))) {
+    if (webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view)))
+      icon = G_ICON (g_themed_icon_new ("ephy-audio-muted-symbolic"));
+    else
+      icon = G_ICON (g_themed_icon_new ("ephy-audio-playing-symbolic"));
+  }
+
+  hdy_tab_page_set_indicator_icon (page, icon);
+}
+
+gint
+ephy_tab_view_add_tab (EphyTabView *self,
+                       EphyEmbed   *embed,
+                       gint         position,
+                       gboolean     jump_to)
+{
+  HdyTabPage *page;
+  EphyWebView *view;
+
+  if (position < 0)
+    page = hdy_tab_view_append (self->tab_view, GTK_WIDGET (embed));
+  else
+    page = hdy_tab_view_insert (self->tab_view, GTK_WIDGET (embed), position);
+
+  if (jump_to)
+    hdy_tab_view_set_selected_page (self->tab_view, page);
+
+  view = ephy_embed_get_web_view (embed);
+
+  hdy_tab_page_set_indicator_activatable (page, TRUE);
+
+  g_object_bind_property (view, "is-loading", page, "loading", G_BINDING_SYNC_CREATE);
+
+  g_signal_connect_object (embed, "notify::title",
+                           G_CALLBACK (update_title_cb), page,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::display-address",
+                           G_CALLBACK (update_title_cb), page,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::icon",
+                           G_CALLBACK (update_icon_cb), page,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::uri",
+                           G_CALLBACK (update_icon_cb), page,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::is-playing-audio",
+                           G_CALLBACK (update_indicator_cb), page,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (view, "notify::is-muted",
+                           G_CALLBACK (update_indicator_cb), page,
+                           G_CONNECT_SWAPPED);
+
+  update_title_cb (page);
+  update_icon_cb (page);
+  update_indicator_cb (page);
+
+  return hdy_tab_view_get_page_position (self->tab_view, page);
+}
+
+GtkWidget *
+ephy_tab_view_get_current_page (EphyTabView *self)
+{
+  return hdy_tab_page_get_child (get_current_page (self));
+}
+
+static void
+drag_data_received_cb (EphyTabView      *self,
+                       HdyTabPage       *page,
+                       GdkDragContext   *context,
+                       GtkSelectionData *selection_data,
+                       guint             info,
+                       guint             time)
+{
+  GtkWidget *window;
+  EphyEmbed *embed;
+  GdkAtom target;
+  const guchar *data;
+
+  if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
+                              EPHY_PREFS_LOCKDOWN_ARBITRARY_URL))
+    return;
+
+  data = gtk_selection_data_get_data (selection_data);
+  if (gtk_selection_data_get_length (selection_data) <= 0 || data == NULL)
+    return;
+
+  embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+  target = gtk_selection_data_get_target (selection_data);
+
+  window = gtk_widget_get_toplevel (GTK_WIDGET (self));
+
+  if (target == gdk_atom_intern (EPHY_DND_URL_TYPE, FALSE)) {
+    char **split;
+
+    /* URL_TYPE has format: url \n title */
+    split = g_strsplit ((const gchar *)data, "\n", 2);
+    if (split != NULL && split[0] != NULL && split[0][0] != '\0') {
+      ephy_link_open (EPHY_LINK (window), NULL, NULL, EPHY_LINK_NEW_TAB);
+      ephy_link_open (EPHY_LINK (window), split[0], embed,
+                      embed ? 0 : EPHY_LINK_NEW_TAB);
+    }
+    g_strfreev (split);
+  } else if (target == gdk_atom_intern (EPHY_DND_URI_LIST_TYPE, FALSE)) {
+    char **uris;
+    int i;
+
+    uris = gtk_selection_data_get_uris (selection_data);
+    if (uris == NULL)
+      return;
+
+    for (i = 0; i < INSANE_NUMBER_OF_URLS && uris[i] != NULL; i++) {
+      embed = ephy_link_open (EPHY_LINK (window), uris[i], embed,
+                              (embed && i == 0) ? 0 : EPHY_LINK_NEW_TAB);
+    }
+
+    g_strfreev (uris);
+  } else {
+    char *text;
+
+    text = (char *)gtk_selection_data_get_text (selection_data);
+    if (text != NULL) {
+      char *address;
+
+      address = ephy_embed_utils_normalize_or_autosearch_address (text);
+      ephy_link_open (EPHY_LINK (window), address, embed,
+                      embed ? 0 : EPHY_LINK_NEW_TAB);
+      g_free (address);
+      g_free (text);
+    }
+  }
+}
+
+static void
+visibility_policy_changed_cb (EphyTabView *self)
+{
+  EphyEmbedShellMode mode;
+  EphyPrefsUITabsBarVisibilityPolicy policy;
+
+  mode = ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (ephy_shell_get_default ()));
+
+  if (is_desktop_pantheon ())
+    policy = EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY_ALWAYS;
+  else
+    policy = g_settings_get_enum (EPHY_SETTINGS_UI,
+                                  EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY);
+
+  hdy_tab_bar_set_autohide (self->tab_bar,
+                            policy != EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY_ALWAYS);
+  gtk_widget_set_visible (GTK_WIDGET (self->tab_bar),
+                          mode != EPHY_EMBED_SHELL_MODE_APPLICATION &&
+                          policy != EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY_NEVER);
+}
+
+static void
+expand_changed_cb (EphyTabView *self)
+{
+  gboolean expand = g_settings_get_boolean (EPHY_SETTINGS_UI,
+                                            EPHY_PREFS_UI_EXPAND_TABS_BAR);
+
+  hdy_tab_bar_set_expand_tabs (self->tab_bar, expand);
+}
+
+static gboolean
+is_layout_reversed (void)
+{
+  GtkSettings *settings;
+  g_autofree char *layout = NULL;
+  g_auto (GStrv) parts = NULL;
+
+  settings = gtk_settings_get_default ();
+  g_object_get (settings, "gtk-decoration-layout", &layout, NULL);
+
+  parts = g_strsplit (layout, ":", 2);
+
+  /* Invalid layout, don't even try */
+  if (g_strv_length (parts) < 2)
+    return FALSE;
+
+  return !!g_strrstr (parts[0], "close");
+}
+
+static void
+notify_decoration_layout_cb (EphyTabView *self)
+{
+  hdy_tab_bar_set_inverted (self->tab_bar, is_layout_reversed ());
+}
+
+void
+ephy_tab_view_set_tab_bar (EphyTabView *self,
+                           HdyTabBar   *tab_bar)
+{
+  GtkTargetList *target_list;
+  GtkSettings *settings;
+  static const GtkTargetEntry url_drag_types [] = {
+    { (char *)EPHY_DND_URI_LIST_TYPE, 0, 0 },
+    { (char *)EPHY_DND_URL_TYPE, 0, 1 },
+  };
+
+  self->tab_bar = tab_bar;
+
+  target_list = gtk_target_list_new (url_drag_types,
+                                     G_N_ELEMENTS (url_drag_types));
+  gtk_target_list_add_text_targets (target_list, 0);
+
+  hdy_tab_bar_set_extra_drag_dest_targets (self->tab_bar, target_list);
+
+  gtk_target_list_unref (target_list);
+
+  g_signal_connect_object (tab_bar, "extra-drag-data-received",
+                           G_CALLBACK (drag_data_received_cb), self,
+                           G_CONNECT_SWAPPED);
+
+  if (is_desktop_pantheon ()) {
+    GtkWidget *button;
+
+    hdy_tab_bar_set_autohide (tab_bar, FALSE);
+    hdy_tab_bar_set_expand_tabs (tab_bar, FALSE);
+
+    button = gtk_button_new_from_icon_name ("list-add-symbolic", GTK_ICON_SIZE_MENU);
+    /* Translators: tooltip for the new tab button */
+    gtk_widget_set_tooltip_text (button, _("Open a new tab"));
+    gtk_actionable_set_action_name (GTK_ACTIONABLE (button), "win.new-tab");
+    gtk_style_context_add_class (gtk_widget_get_style_context (button), "flat");
+    gtk_widget_show (button);
+
+    hdy_tab_bar_set_start_action_widget (tab_bar, button);
+  } else {
+    g_signal_connect_object (EPHY_SETTINGS_UI,
+                             "changed::" EPHY_PREFS_UI_TABS_BAR_VISIBILITY_POLICY,
+                             G_CALLBACK (visibility_policy_changed_cb), self,
+                             G_CONNECT_SWAPPED);
+
+    g_signal_connect_object (EPHY_SETTINGS_UI,
+                             "changed::" EPHY_PREFS_UI_EXPAND_TABS_BAR,
+                             G_CALLBACK (expand_changed_cb), self,
+                             G_CONNECT_SWAPPED);
+  }
+
+  settings = gtk_settings_get_default ();
+  g_signal_connect_object (settings, "notify::gtk-decoration-layout",
+                           G_CALLBACK (notify_decoration_layout_cb), self,
+                           G_CONNECT_SWAPPED);
+
+  visibility_policy_changed_cb (self);
+}
diff --git a/src/ephy-tab-view.h b/src/ephy-tab-view.h
new file mode 100644
index 000000000..a04607ef6
--- /dev/null
+++ b/src/ephy-tab-view.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 2; indent-pages-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2020 Alexander Mikhaylenko <exalm7659 gmail com>
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <handy.h>
+#include "ephy-embed.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_TAB_VIEW (ephy_tab_view_get_type())
+
+G_DECLARE_FINAL_TYPE (EphyTabView, ephy_tab_view, EPHY, TAB_VIEW, GtkBin)
+
+EphyTabView *ephy_tab_view_new                (void);
+
+void         ephy_tab_view_next               (EphyTabView *self);
+
+void         ephy_tab_view_pin                (EphyTabView *self);
+void         ephy_tab_view_unpin              (EphyTabView *self);
+
+void         ephy_tab_view_close              (EphyTabView *self,
+                                               GtkWidget   *widget);
+void         ephy_tab_view_close_selected     (EphyTabView *self);
+void         ephy_tab_view_close_left         (EphyTabView *self);
+void         ephy_tab_view_close_right        (EphyTabView *self);
+void         ephy_tab_view_close_other        (EphyTabView *self);
+
+void         ephy_tab_view_foreach            (EphyTabView *self,
+                                               GtkCallback  callback,
+                                               gpointer     user_data);
+
+gint         ephy_tab_view_get_n_pages        (EphyTabView *self);
+gint         ephy_tab_view_get_selected_index (EphyTabView *self);
+gint         ephy_tab_view_get_page_index     (EphyTabView *self,
+                                               GtkWidget   *widget);
+
+GtkWidget   *ephy_tab_view_get_nth_page       (EphyTabView *self,
+                                               gint         index);
+void         ephy_tab_view_select_nth_page    (EphyTabView *self,
+                                               gint         index);
+
+gboolean     ephy_tab_view_select_page        (EphyTabView *self,
+                                               GtkWidget   *widget);
+
+GtkWidget   *ephy_tab_view_get_selected_page  (EphyTabView *self);
+
+HdyTabView  *ephy_tab_view_get_tab_view       (EphyTabView *self);
+
+GList        *ephy_tab_view_get_pages         (EphyTabView *self);
+
+gboolean      ephy_tab_view_get_is_pinned     (EphyTabView *self,
+                                               GtkWidget   *widget);
+
+gint          ephy_tab_view_add_tab           (EphyTabView *self,
+                                               EphyEmbed   *embed,
+                                               gint         position,
+                                               gboolean     jump_to);
+
+GtkWidget    *ephy_tab_view_get_current_page  (EphyTabView *self);
+
+void          ephy_tab_view_set_tab_bar       (EphyTabView *self,
+                                               HdyTabBar   *tab_bar);
+
+G_END_DECLS
diff --git a/src/ephy-window.c b/src/ephy-window.c
index a34bdd280..eb0bbcd94 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -45,7 +45,6 @@
 #include "ephy-link.h"
 #include "ephy-location-entry.h"
 #include "ephy-mouse-gesture-controller.h"
-#include "ephy-notebook.h"
 #include "ephy-pages-view.h"
 #include "ephy-permissions-manager.h"
 #include "ephy-prefs.h"
@@ -130,10 +129,6 @@ const struct {
   { "toolbar.combined-stop-reload", { NULL } },
 
   /* Tabs */
-  { "tab.previous", { "<Primary>Page_Up", "<Primary>KP_9", "<shift><Primary>Tab", NULL } },
-  { "tab.next", { "<Primary>Page_Down", "<Primary>KP_3", "<Primary>Tab", NULL } },
-  { "tab.move-left", { "<shift><Primary>Page_Up", NULL } },
-  { "tab.move-right", { "<shift><Primary>Page_Down", NULL } },
   { "tab.duplicate", { "<shift><Primary>K", NULL } },
   { "tab.close", { "<Primary>W", NULL } },
   { "tab.mute", { "<Primary>M", NULL } },
@@ -154,11 +149,14 @@ struct _EphyWindow {
   GtkWidget *main_deck;
   EphyFullscreenBox *fullscreen_box;
   GtkWidget *window_handle;
+  GtkBox *titlebar_box;
   GtkWidget *header_bar;
   EphyPagesView *pages_view;
   EphyBookmarksManager *bookmarks_manager;
   GHashTable *action_labels;
-  GtkNotebook *notebook;
+  EphyTabView *tab_view;
+  HdyTabBar *tab_bar;
+  GtkRevealer *tab_bar_revealer;
   GtkWidget *action_bar;
   EphyEmbed *active_embed;
   EphyWindowChrome chrome;
@@ -220,24 +218,25 @@ impl_add_child (EphyEmbedContainer *container,
                 gboolean            jump_to)
 {
   EphyWindow *window = EPHY_WINDOW (container);
+  int ret;
 
-  g_assert (!window->is_popup || gtk_notebook_get_n_pages (GTK_NOTEBOOK (window->notebook)) < 1);
+  g_assert (!window->is_popup || ephy_tab_view_get_n_pages (window->tab_view) < 1);
 
-  return ephy_notebook_add_tab (EPHY_NOTEBOOK (window->notebook),
-                                child, position, jump_to);
+  ret = ephy_tab_view_add_tab (window->tab_view, child, position, jump_to);
+
+  if (jump_to)
+    ephy_window_update_entry_focus (window, ephy_embed_get_web_view (child));
+
+  return ret;
 }
 
 static void
 impl_set_active_child (EphyEmbedContainer *container,
                        EphyEmbed          *child)
 {
-  int page;
-  EphyWindow *window;
-
-  window = EPHY_WINDOW (container);
+  EphyWindow *window = EPHY_WINDOW (container);
 
-  page = gtk_notebook_page_num (window->notebook, GTK_WIDGET (child));
-  gtk_notebook_set_current_page (window->notebook, page);
+  ephy_tab_view_select_page (window->tab_view, GTK_WIDGET (child));
 }
 
 static GtkWidget *
@@ -307,12 +306,9 @@ static void
 impl_remove_child (EphyEmbedContainer *container,
                    EphyEmbed          *child)
 {
-  EphyWindow *window;
+  EphyWindow *window = EPHY_WINDOW (container);
 
-  window = EPHY_WINDOW (container);
-  g_signal_emit_by_name (window->notebook,
-                         "tab-close-request",
-                         child, window);
+  ephy_tab_view_close (window->tab_view, GTK_WIDGET (child));
 }
 
 static EphyEmbed *
@@ -326,7 +322,7 @@ impl_get_children (EphyEmbedContainer *container)
 {
   EphyWindow *window = EPHY_WINDOW (container);
 
-  return gtk_container_get_children (GTK_CONTAINER (window->notebook));
+  return ephy_tab_view_get_pages (window->tab_view);
 }
 
 static gboolean
@@ -340,7 +336,7 @@ impl_get_n_children (EphyEmbedContainer *container)
 {
   EphyWindow *window = EPHY_WINDOW (container);
 
-  return gtk_notebook_get_n_pages (window->notebook);
+  return ephy_tab_view_get_n_pages (window->tab_view);
 }
 
 static void
@@ -444,8 +440,8 @@ sync_chromes_visibility (EphyWindow *window)
 
   show_tabsbar = (window->chrome & EPHY_WINDOW_CHROME_TABSBAR);
 
-  ephy_notebook_set_tabs_allowed (EPHY_NOTEBOOK (window->notebook),
-                                  show_tabsbar && !(window->is_popup || window->is_fullscreen));
+  gtk_widget_set_visible (GTK_WIDGET (window->tab_bar_revealer),
+                          show_tabsbar && !(window->is_popup));
 }
 
 static void
@@ -518,7 +514,6 @@ update_adaptive_mode (EphyWindow *window)
 {
   EphyHeaderBar *header_bar = EPHY_HEADER_BAR (ephy_window_get_header_bar (window));
   EphyActionBar *action_bar = EPHY_ACTION_BAR (window->action_bar);
-  EphyNotebook *notebook = EPHY_NOTEBOOK (window->notebook);
   gboolean is_narrow, is_mobile_landscape;
   EphyAdaptiveMode adaptive_mode;
   gint width, height;
@@ -551,7 +546,9 @@ update_adaptive_mode (EphyWindow *window)
                   EPHY_ADAPTIVE_MODE_NORMAL;
   ephy_header_bar_set_adaptive_mode (header_bar, adaptive_mode);
   ephy_action_bar_set_adaptive_mode (action_bar, adaptive_mode);
-  ephy_notebook_set_adaptive_mode (notebook, adaptive_mode);
+
+  gtk_revealer_set_reveal_child (window->tab_bar_revealer,
+                                 adaptive_mode == EPHY_ADAPTIVE_MODE_NORMAL);
 
   /* When switching to desktop sizes, drop the tabs view and go back
    * to the main view.
@@ -574,7 +571,6 @@ ephy_window_fullscreen (EphyWindow *window)
   sync_tab_security (ephy_embed_get_web_view (embed), NULL, window);
 
   update_adaptive_mode (window);
-  sync_chromes_visibility (window);
   ephy_embed_entering_fullscreen (embed);
 }
 
@@ -585,7 +581,6 @@ ephy_window_unfullscreen (EphyWindow *window)
   g_object_notify (G_OBJECT (window), "fullscreen");
 
   update_adaptive_mode (window);
-  sync_chromes_visibility (window);
   ephy_embed_leaving_fullscreen (window->active_embed);
 }
 
@@ -876,22 +871,16 @@ static const GActionEntry window_entries [] = {
   { "content", window_cmd_go_content },
   { "tabs-view", window_cmd_go_tabs_view },
 
-  { "show-tab", window_cmd_show_tab, "u", "uint32 0", window_cmd_change_show_tab_state },
-
   /* Toggle actions */
   { "browse-with-caret", NULL, NULL, "false", window_cmd_change_browse_with_caret_state },
   { "fullscreen", NULL, NULL, "false", window_cmd_change_fullscreen_state },
 };
 
 static const GActionEntry tab_entries [] = {
-  { "previous", window_cmd_tabs_previous },
-  { "next", window_cmd_tabs_next },
   { "duplicate", window_cmd_tabs_duplicate },
   { "close", window_cmd_tabs_close },
   { "close-left", window_cmd_tabs_close_left },
   { "close-right", window_cmd_tabs_close_right },
-  { "move-left", window_cmd_tabs_move_left },
-  { "move-right", window_cmd_tabs_move_right },
   { "close-others", window_cmd_tabs_close_others },
   { "reload-all", window_cmd_tabs_reload_all_tabs },
   { "pin", window_cmd_tabs_pin },
@@ -2586,218 +2575,68 @@ ephy_window_set_active_tab (EphyWindow *window,
 }
 
 static void
-tab_accels_item_activate (GSimpleAction *action,
-                          GVariant      *parameter,
-                          gpointer       user_data)
+tab_view_setup_menu_cb (HdyTabView *tab_view,
+                        HdyTabPage *page,
+                        EphyWindow *window)
 {
-  const char *action_name;
-  int tab_number;
-
-  action_name = g_action_get_name (G_ACTION (action));
-
-  tab_number = atoi (action_name + strlen ("accel-"));
-
-  gtk_notebook_set_current_page (EPHY_WINDOW (user_data)->notebook, tab_number);
-}
-
-static void
-tab_accels_update (EphyWindow *window)
-{
-  int n_pages;
-  int i = 0;
-  GActionGroup *action_group;
-  char **actions;
-
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "tab");
-  actions = g_action_group_list_actions (action_group);
-
-  n_pages = gtk_notebook_get_n_pages (window->notebook);
-  for (i = 0; actions[i] != NULL; i++) {
-    if (strstr (actions[i], "accel-") != NULL) {
-      GAction *action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                                    actions[i]);
-      int tab_number = atoi (actions[i] + strlen ("accel-"));
-
-      g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (tab_number < n_pages));
-    }
-  }
-
-  g_strfreev (actions);
-}
-
-static void
-last_tab_accel_activate (GSimpleAction *action,
-                         GVariant      *parameter,
-                         gpointer       user_data)
-{
-  EphyWindow *window = EPHY_WINDOW (user_data);
-  EphyNotebook *notebook = EPHY_NOTEBOOK (window->notebook);
-
-  ephy_notebook_switch_to_last_tab (notebook);
-}
-
-#define TAB_ACCELS_N 10
-
-static void
-setup_tab_accels (EphyWindow *window)
-{
-  GActionGroup *action_group;
-  GApplication *app;
-  guint i;
-  GSimpleAction *last_tab_action;
-
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "tab");
-  app = g_application_get_default ();
-
-  for (i = 0; i < TAB_ACCELS_N - 1; i++) {
-    GSimpleAction *simple_action;
-    char *action_name;
-    char *action_name_with_tab;
-    char *accel;
-
-    action_name = g_strdup_printf ("accel-%u", i);
-    action_name_with_tab = g_strconcat ("tab.", action_name, NULL);
-    accel = g_strdup_printf ("<alt>%u", (i + 1) % TAB_ACCELS_N);
-
-    simple_action = g_simple_action_new (action_name, NULL);
-
-    g_action_map_add_action (G_ACTION_MAP (action_group), G_ACTION (simple_action));
-    gtk_application_set_accels_for_action (GTK_APPLICATION (app),
-                                           action_name_with_tab,
-                                           (const gchar *[]) {accel, NULL});
-
-    g_signal_connect (G_ACTION (simple_action), "activate",
-                      G_CALLBACK (tab_accels_item_activate), window);
-
-    g_object_unref (simple_action);
-    g_free (accel);
-    g_free (action_name);
-    g_free (action_name_with_tab);
-  }
-
-  last_tab_action = g_simple_action_new ("switch-to-last-tab", NULL);
-  g_action_map_add_action (G_ACTION_MAP (action_group), G_ACTION (last_tab_action));
-  gtk_application_set_accels_for_action (GTK_APPLICATION (app),
-                                         "tab.switch-to-last-tab",
-                                         (const gchar *[]) {"<alt>0", NULL});
-
-  g_signal_connect (G_ACTION (last_tab_action), "activate",
-                    G_CALLBACK (last_tab_accel_activate), window);
-  g_object_unref (last_tab_action);
-}
-
-static gboolean
-show_notebook_popup_menu (GtkNotebook    *notebook,
-                          EphyWindow     *window,
-                          GdkEventButton *event)
-{
-  GtkWidget *menu, *tab, *tab_label;
-  GMenu *menu_model;
-  GtkBuilder *builder;
+  EphyWebView *view;
   GActionGroup *action_group;
   GAction *action;
+  int n_pages;
+  int n_pinned_pages;
+  int position;
+  gboolean pinned;
+  gboolean audio_playing;
+  gboolean muted;
 
-  builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/notebook-context-menu.ui");
-
-  menu_model = G_MENU (gtk_builder_get_object (builder, "notebook-menu"));
-  menu = gtk_menu_new_from_model (G_MENU_MODEL (menu_model));
-  gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (window->active_embed), NULL);
-
-  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "tab");
-
-  if (event != NULL) {
-    EphyWebView *view;
-    int n_pages;
-    int page_num;
-    gboolean pinned;
-    gboolean audio_playing;
-    gboolean muted;
-
-    tab = GTK_WIDGET (window->active_embed);
-    n_pages = gtk_notebook_get_n_pages (notebook);
-    page_num = gtk_notebook_page_num (notebook, tab);
-    pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), EPHY_EMBED (tab));
+  if (page) {
+    n_pages = hdy_tab_view_get_n_pages (tab_view);
+    n_pinned_pages = hdy_tab_view_get_n_pinned_pages (tab_view);
+    position = hdy_tab_view_get_page_position (tab_view, page);
+    pinned = hdy_tab_page_get_pinned (page);
 
-    view = ephy_embed_get_web_view (EPHY_EMBED (tab));
+    view = ephy_embed_get_web_view (EPHY_EMBED (hdy_tab_page_get_child (page)));
     audio_playing = webkit_web_view_is_playing_audio (WEBKIT_WEB_VIEW (view));
     muted = webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view));
+  }
 
-    /* enable/disable close others/left/right */
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "close-left");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (page_num > 0) && !pinned);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "close-right");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (page_num < n_pages - 1) && !pinned);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "close-others");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (n_pages > 1) && !pinned);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "reload-all");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), n_pages > 1);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "pin");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !pinned);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "unpin");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), pinned);
-
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "mute");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), audio_playing);
-    g_simple_action_set_state (G_SIMPLE_ACTION (action),
-                               g_variant_new_boolean (muted));
+  action_group = gtk_widget_get_action_group (GTK_WIDGET (window), "tab");
 
-    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
-                                         "close");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !pinned);
+  /* enable/disable close others/left/right */
+  /* If there's no page, enable all actions so that we don't interfere with hotkeys */
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "close-left");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || position > n_pinned_pages);
 
-    gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event);
-  } else {
-    tab = GTK_WIDGET (window->active_embed);
-    tab_label = gtk_notebook_get_tab_label (notebook, tab);
-
-    /* Not tested, because I don't know how to trigger this code. */
-    gtk_menu_popup_at_widget (GTK_MENU (menu),
-                              tab_label,
-                              GDK_GRAVITY_SOUTH_WEST,
-                              GDK_GRAVITY_NORTH_WEST,
-                              NULL);
-    gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
-  }
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "close-right");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || (position < n_pages - 1 && !pinned));
 
-  g_object_unref (builder);
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "close-others");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || (n_pages > n_pinned_pages + 1 && !pinned));
 
-  return TRUE;
-}
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "reload-all");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || n_pages > 1);
 
-static gboolean
-notebook_button_press_cb (GtkNotebook    *notebook,
-                          GdkEventButton *event,
-                          EphyWindow     *window)
-{
-  if (GDK_BUTTON_PRESS == event->type && 3 == event->button) {
-    return show_notebook_popup_menu (notebook, window, event);
-  }
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "pin");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || !pinned);
 
-  return FALSE;
-}
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "unpin");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || pinned);
 
-static gboolean
-notebook_popup_menu_cb (GtkNotebook *notebook,
-                        EphyWindow  *window)
-{
-  /* Only respond if the notebook is the actual focus */
-  if (EPHY_IS_NOTEBOOK (gtk_window_get_focus (GTK_WINDOW (window)))) {
-    return show_notebook_popup_menu (notebook, window, NULL);
-  }
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "mute");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || audio_playing);
+  g_simple_action_set_state (G_SIMPLE_ACTION (action),
+                             g_variant_new_boolean (!page || muted));
 
-  return FALSE;
+  action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                       "close");
+  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !page || !pinned);
 }
 
 static gboolean
@@ -2829,7 +2668,7 @@ download_only_load_cb (EphyWebView *view,
   if (ephy_web_view_get_document_type (view) == EPHY_WEB_VIEW_DOCUMENT_PDF)
     return;
 
-  if (gtk_notebook_get_n_pages (window->notebook) == 1) {
+  if (ephy_tab_view_get_n_pages (window->tab_view) == 1) {
     ephy_web_view_load_homepage (view);
     return;
   }
@@ -2869,14 +2708,19 @@ reader_mode_cb (EphyWebView *view,
 }
 
 static void
-notebook_page_added_cb (EphyNotebook *notebook,
-                        EphyEmbed    *embed,
-                        guint         position,
-                        EphyWindow   *window)
+tab_view_page_attached_cb (HdyTabView *tab_view,
+                           HdyTabPage *page,
+                           gint        position,
+                           EphyWindow *window)
 {
-  LOG ("page-added notebook %p embed %p position %u\n", notebook, embed, position);
+  GtkWidget *content = hdy_tab_page_get_child (page);
+  EphyEmbed *embed;
 
-  g_assert (EPHY_IS_EMBED (embed));
+  g_assert (EPHY_IS_EMBED (content));
+
+  embed = EPHY_EMBED (content);
+
+  LOG ("page-attached tab view %p embed %p position %d\n", tab_view, embed, position);
 
   g_signal_connect_object (ephy_embed_get_web_view (embed), "download-only-load",
                            G_CALLBACK (download_only_load_cb), window, G_CONNECT_AFTER);
@@ -2888,29 +2732,27 @@ notebook_page_added_cb (EphyNotebook *notebook,
     window->present_on_insert = FALSE;
     g_idle_add ((GSourceFunc)present_on_idle_cb, g_object_ref (window));
   }
-
-  tab_accels_update (window);
 }
 
 static void
-notebook_page_removed_cb (EphyNotebook *notebook,
-                          EphyEmbed    *embed,
-                          guint         position,
-                          EphyWindow   *window)
+tab_view_page_detached_cb (HdyTabView *tab_view,
+                           HdyTabPage *page,
+                           gint        position,
+                           EphyWindow *window)
 {
-  LOG ("page-removed notebook %p embed %p position %u\n", notebook, embed, position);
+  GtkWidget *content = hdy_tab_page_get_child (page);
+
+  LOG ("page-detached tab view %p embed %p position %d\n", tab_view, content, position);
 
   if (window->closing)
     return;
 
-  g_assert (EPHY_IS_EMBED (embed));
+  g_assert (EPHY_IS_EMBED (content));
 
   g_signal_handlers_disconnect_by_func
-    (ephy_embed_get_web_view (embed), G_CALLBACK (download_only_load_cb), window);
-
-  tab_accels_update (window);
+    (ephy_embed_get_web_view (EPHY_EMBED (content)), G_CALLBACK (download_only_load_cb), window);
 
-  if (gtk_notebook_get_n_pages (window->notebook) == 0) {
+  if (ephy_tab_view_get_n_pages (window->tab_view) == 0) {
     EphyShell *shell = ephy_shell_get_default ();
     GList *windows = gtk_application_get_windows (GTK_APPLICATION (shell));
 
@@ -2951,7 +2793,7 @@ ephy_window_close_tab (EphyWindow *window,
   if (mode != EPHY_EMBED_SHELL_MODE_AUTOMATION)
     keep_window_open = g_settings_get_boolean (EPHY_SETTINGS_UI, EPHY_PREFS_UI_KEEP_WINDOW_OPEN);
 
-  if (keep_window_open && gtk_notebook_get_n_pages (window->notebook) == 1) {
+  if (keep_window_open && ephy_tab_view_get_n_pages (window->tab_view) == 1) {
     EphyWebView *view = ephy_embed_get_web_view (tab);
 
     if (ephy_web_view_get_is_blank (view) || ephy_web_view_is_overview (view))
@@ -2961,7 +2803,6 @@ ephy_window_close_tab (EphyWindow *window,
   }
 
   g_object_set_data (G_OBJECT (tab), "ephy-window-close-tab-closed", GINT_TO_POINTER (TRUE));
-  gtk_widget_destroy (GTK_WIDGET (tab));
 
   if (window->last_opened_embed)
     g_clear_weak_pointer ((gpointer *)&window->last_opened_embed);
@@ -2972,26 +2813,30 @@ ephy_window_close_tab (EphyWindow *window,
    * Beware: window->closing could be true now, after destroying the
    * tab, even if it wasn't at the start of this function.
    */
-  if (!window->closing && gtk_notebook_get_n_pages (window->notebook) == 0)
+  if (!window->closing && ephy_tab_view_get_n_pages (window->tab_view) == 0)
     gtk_widget_destroy (GTK_WIDGET (window));
 }
 
 typedef struct {
   EphyWindow *window;
   EphyEmbed *embed;
+  HdyTabPage *page;
   guint id;
 } TabHasModifiedFormsData;
 
 static TabHasModifiedFormsData *
 tab_has_modified_forms_data_new (EphyWindow *window,
-                                 EphyEmbed  *embed)
+                                 EphyEmbed  *embed,
+                                 HdyTabPage *page)
 {
   TabHasModifiedFormsData *data = g_new (TabHasModifiedFormsData, 1);
   data->window = window;
   data->embed = embed;
+  data->page = page;
   data->id = 0;
   g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&data->window);
   g_object_add_weak_pointer (G_OBJECT (embed), (gpointer *)&data->embed);
+  g_object_add_weak_pointer (G_OBJECT (page), (gpointer *)&data->page);
   return data;
 }
 
@@ -3000,6 +2845,7 @@ tab_has_modified_forms_data_free (TabHasModifiedFormsData *data)
 {
   g_clear_weak_pointer (&data->window);
   g_clear_weak_pointer (&data->embed);
+  g_clear_weak_pointer (&data->page);
   g_clear_handle_id (&data->id, g_source_remove);
   g_clear_pointer (&data, g_free);
 }
@@ -3021,8 +2867,14 @@ tab_has_modified_forms_cb (EphyWebView             *view,
   if (data->id != 0 &&
       data->window != NULL &&
       data->embed != NULL &&
-      (!has_modified_forms || confirm_close_with_modified_forms (data->window))) {
-    ephy_window_close_tab (data->window, data->embed);
+      data->page != NULL) {
+    HdyTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
+
+    if (!has_modified_forms || confirm_close_with_modified_forms (data->window)) {
+      ephy_window_close_tab (data->window, data->embed);
+      hdy_tab_view_close_page_finish (tab_view, data->page, TRUE);
+    } else
+      hdy_tab_view_close_page_finish (tab_view, data->page, FALSE);
   }
 
   data->id = 0;
@@ -3039,9 +2891,17 @@ tab_has_modified_forms_timeout_cb (TabHasModifiedFormsData *data)
    * ephy_window_close_tab().
    */
   data->id = 0;
-  if (data->window != NULL && data->embed != NULL)
+  if (data->window != NULL &&
+      data->embed != NULL &&
+      data->page != NULL) {
+    HdyTabView *tab_view = ephy_tab_view_get_tab_view (data->window->tab_view);
+    HdyTabPage *page = data->page;
+
     ephy_window_close_tab (data->window, data->embed);
 
+    hdy_tab_view_close_page_finish (tab_view, page, TRUE);
+  }
+
   return G_SOURCE_REMOVE;
 }
 
@@ -3066,15 +2926,21 @@ run_downloads_in_background (EphyWindow *window,
   gtk_widget_hide (GTK_WIDGET (window));
 }
 
-static void
-notebook_page_close_request_cb (EphyNotebook *notebook,
-                                EphyEmbed    *embed,
-                                EphyWindow   *window)
+static gboolean
+tab_view_close_page_cb (HdyTabView *tab_view,
+                        HdyTabPage *page,
+                        EphyWindow *window)
 {
-  if (gtk_notebook_get_n_pages (window->notebook) == 1) {
+  EphyEmbed *embed = EPHY_EMBED (hdy_tab_page_get_child (page));
+
+  if (hdy_tab_page_get_pinned (page))
+    return GDK_EVENT_PROPAGATE;
+
+  if (ephy_tab_view_get_n_pages (window->tab_view) == 1) {
     if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                                 EPHY_PREFS_LOCKDOWN_QUIT)) {
-      return;
+      hdy_tab_view_close_page_finish (tab_view, page, FALSE);
+      return GDK_EVENT_STOP;
     }
 
     if (ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) == EPHY_EMBED_SHELL_MODE_AUTOMATION) {
@@ -3089,7 +2955,8 @@ notebook_page_close_request_cb (EphyNotebook *notebook,
       if (ephy_downloads_manager_has_active_downloads (manager)) {
         GList *list = ephy_downloads_manager_get_downloads (manager);
         run_downloads_in_background (window, g_list_length (list));
-        return;
+        hdy_tab_view_close_page_finish (tab_view, page, FALSE);
+        return GDK_EVENT_STOP;
       }
     }
   }
@@ -3104,23 +2971,23 @@ notebook_page_close_request_cb (EphyNotebook *notebook,
      * also be caused by a network process hang!) We'll assume the process has
      * been hung if there's no response after one second.
      */
-    data = tab_has_modified_forms_data_new (window, embed);
+    data = tab_has_modified_forms_data_new (window, embed, page);
     data->id = g_timeout_add_seconds (1, (GSourceFunc)tab_has_modified_forms_timeout_cb, data);
     ephy_web_view_has_modified_forms (ephy_embed_get_web_view (embed),
                                       NULL,
                                       (GAsyncReadyCallback)tab_has_modified_forms_cb,
                                       data);
+    return GDK_EVENT_STOP;
   } else {
     ephy_window_close_tab (window, embed);
   }
+
+  return GDK_EVENT_PROPAGATE;
 }
 
-static GtkWidget *
-notebook_create_window_cb (GtkNotebook *notebook,
-                           GtkWidget   *page,
-                           int          x,
-                           int          y,
-                           EphyWindow  *window)
+static HdyTabView *
+tab_view_create_window_cb (HdyTabView *tab_view,
+                           EphyWindow *window)
 {
   EphyWindow *new_window;
 
@@ -3129,24 +2996,7 @@ notebook_create_window_cb (GtkNotebook *notebook,
   new_window->present_on_insert = TRUE;
   new_window->present_on_insert_user_time = gtk_get_current_event_time ();
 
-  return ephy_window_get_notebook (new_window);
-}
-
-static EphyEmbed *
-real_get_active_tab (EphyWindow *window,
-                     int         page_num)
-{
-  GtkWidget *embed;
-
-  if (page_num == -1) {
-    page_num = gtk_notebook_get_current_page (window->notebook);
-  }
-
-  embed = gtk_notebook_get_nth_page (window->notebook, page_num);
-
-  g_assert (EPHY_IS_EMBED (embed));
-
-  return EPHY_EMBED (embed);
+  return ephy_tab_view_get_tab_view (new_window->tab_view);
 }
 
 void
@@ -3169,81 +3019,82 @@ ephy_window_update_entry_focus (EphyWindow  *window,
 }
 
 static void
-notebook_switch_page_cb (GtkNotebook *notebook,
-                         GtkWidget   *page,
-                         guint        page_num,
-                         EphyWindow  *window)
+tab_view_notify_selected_page_cb (EphyWindow *window)
 {
   EphyEmbed *embed;
-  GActionGroup *group;
-  GAction *action;
   EphyWebView *view;
-
-  LOG ("switch-page notebook %p position %u\n", notebook, page_num);
+  int page_num;
 
   if (window->closing)
     return;
 
+  page_num = ephy_tab_view_get_selected_index (window->tab_view);
+
+  if (page_num < 0)
+    return;
+
+  LOG ("switch-page tab view %p position %d\n", window->tab_view, page_num);
+
   /* get the new tab */
-  embed = real_get_active_tab (window, page_num);
+
+  embed = EPHY_EMBED (ephy_tab_view_get_nth_page (window->tab_view, page_num));
   view = ephy_embed_get_web_view (embed);
 
   /* update new tab */
   ephy_window_set_active_tab (window, embed);
 
-  /* update notebook menu */
-  group = gtk_widget_get_action_group (GTK_WIDGET (window), "win");
-  action = g_action_map_lookup_action (G_ACTION_MAP (group), "show-tab");
-  g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_uint32 (page_num));
-
   update_reader_mode (window, view);
-
-  ephy_window_update_entry_focus (window, view);
 }
 
 static void
-notebook_page_reordered_cb (GtkNotebook *notebook,
-                            GtkWidget   *child,
-                            guint        page_num,
-                            gpointer     user_data)
+tab_view_page_reordered_cb (EphyWindow *window)
 {
-  EphyWindow *window = EPHY_WINDOW (user_data);
-
   window->last_opened_embed = NULL;
 }
 
-static GtkNotebook *
-setup_notebook (EphyWindow *window)
+static EphyTabView *
+setup_tab_view (EphyWindow *window)
 {
-  GtkNotebook *notebook;
-
-  notebook = GTK_NOTEBOOK (g_object_new (EPHY_TYPE_NOTEBOOK, NULL));
-
-  g_signal_connect_after (notebook, "switch-page",
-                          G_CALLBACK (notebook_switch_page_cb),
-                          window);
-  g_signal_connect (notebook, "create-window",
-                    G_CALLBACK (notebook_create_window_cb),
-                    window);
+  EphyTabView *tab_view = ephy_tab_view_new ();
+  HdyTabView *view = ephy_tab_view_get_tab_view (tab_view);
+  g_autoptr (GtkBuilder) builder = NULL;
 
-  g_signal_connect (notebook, "popup-menu",
-                    G_CALLBACK (notebook_popup_menu_cb), window);
-  g_signal_connect (notebook, "button-press-event",
-                    G_CALLBACK (notebook_button_press_cb), window);
-
-  g_signal_connect (notebook, "page-added",
-                    G_CALLBACK (notebook_page_added_cb), window);
-  g_signal_connect (notebook, "page-removed",
-                    G_CALLBACK (notebook_page_removed_cb), window);
-  g_signal_connect (notebook, "tab-close-request",
-                    G_CALLBACK (notebook_page_close_request_cb), window);
-  g_signal_connect (notebook, "page-reordered",
-                    G_CALLBACK (notebook_page_reordered_cb), window);
-
-  g_signal_connect_swapped (notebook, "open-link",
-                            G_CALLBACK (ephy_link_open), window);
+  builder = gtk_builder_new_from_resource ("/org/gnome/epiphany/gtk/notebook-context-menu.ui");
 
-  return notebook;
+  hdy_tab_view_set_menu_model (view, G_MENU_MODEL (gtk_builder_get_object (builder, "notebook-menu")));
+
+  g_signal_connect_object (view, "notify::selected-page",
+                           G_CALLBACK (tab_view_notify_selected_page_cb),
+                           window,
+                           G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (view, "create-window",
+                           G_CALLBACK (tab_view_create_window_cb),
+                           window,
+                           0);
+  g_signal_connect_object (view, "setup-menu",
+                           G_CALLBACK (tab_view_setup_menu_cb),
+                           window,
+                           0);
+  g_signal_connect_object (view, "close-page",
+                           G_CALLBACK (tab_view_close_page_cb),
+                           window,
+                           0);
+
+  g_signal_connect_object (view, "page-attached",
+                           G_CALLBACK (tab_view_page_attached_cb),
+                           window,
+                           0);
+  g_signal_connect_object (view, "page-detached",
+                           G_CALLBACK (tab_view_page_detached_cb),
+                           window,
+                           0);
+  g_signal_connect_object (view, "page-reordered",
+                           G_CALLBACK (tab_view_page_reordered_cb),
+                           window,
+                           G_CONNECT_SWAPPED);
+
+  return tab_view;
 }
 
 static void
@@ -3346,10 +3197,8 @@ ephy_window_state_event (GtkWidget           *widget,
       ephy_window_unfullscreen (window);
     }
 
-    ephy_fullscreen_box_set_fullscreen (window->fullscreen_box,
-                                        fullscreen && window->show_fullscreen_header_bar);
-    gtk_widget_set_visible (GTK_WIDGET (window->window_handle),
-                            !fullscreen || window->show_fullscreen_header_bar);
+    ephy_fullscreen_box_set_fullscreen (window->fullscreen_box, fullscreen && 
window->show_fullscreen_header_bar);
+    gtk_widget_set_visible (GTK_WIDGET (window->titlebar_box), !fullscreen || 
window->show_fullscreen_header_bar);
 
     window->show_fullscreen_header_bar = FALSE;
 
@@ -3823,15 +3672,16 @@ ephy_window_constructed (GObject *object)
 
   ephy_gui_ensure_window_group (GTK_WINDOW (window));
 
-  /* Setup tab accels */
-  setup_tab_accels (window);
-
-  window->notebook = setup_notebook (window);
+  window->tab_view = setup_tab_view (window);
+  window->tab_bar = hdy_tab_bar_new ();
+  window->tab_bar_revealer = GTK_REVEALER (gtk_revealer_new ());
   window->main_deck = hdy_deck_new ();
   window->fullscreen_box = ephy_fullscreen_box_new ();
   window->pages_view = ephy_pages_view_new ();
 
-  ephy_pages_view_set_notebook (window->pages_view, EPHY_NOTEBOOK (window->notebook));
+  gtk_revealer_set_transition_type (window->tab_bar_revealer, GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
+  hdy_tab_bar_set_view (window->tab_bar, ephy_tab_view_get_tab_view (window->tab_view));
+  ephy_pages_view_set_tab_view (window->pages_view, window->tab_view);
 
   shell = ephy_shell_get_default ();
   mode = ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (shell));
@@ -3847,16 +3697,20 @@ ephy_window_constructed (GObject *object)
   window->location_controller = setup_location_controller (window, EPHY_HEADER_BAR (window->header_bar));
   window->action_bar = setup_action_bar (window);
   box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
+  window->titlebar_box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
 
   if (g_settings_get_boolean (EPHY_SETTINGS_MAIN, EPHY_PREFS_ASK_FOR_DEFAULT) &&
       !is_browser_default () &&
       !ephy_profile_dir_is_web_application ())
     add_default_browser_question (box);
 
-  gtk_box_pack_start (box, GTK_WIDGET (window->notebook), TRUE, TRUE, 0);
+  gtk_container_add (GTK_CONTAINER (window->tab_bar_revealer), GTK_WIDGET (window->tab_bar));
+  gtk_box_pack_start (window->titlebar_box, GTK_WIDGET (window->window_handle), FALSE, TRUE, 0);
+  gtk_box_pack_start (window->titlebar_box, GTK_WIDGET (window->tab_bar_revealer), FALSE, TRUE, 0);
+  gtk_box_pack_start (box, GTK_WIDGET (window->tab_view), TRUE, TRUE, 0);
   gtk_box_pack_start (box, GTK_WIDGET (window->action_bar), FALSE, TRUE, 0);
   gtk_container_add (GTK_CONTAINER (window->fullscreen_box), GTK_WIDGET (box));
-  ephy_fullscreen_box_set_titlebar (window->fullscreen_box, GTK_WIDGET (window->window_handle));
+  ephy_fullscreen_box_set_titlebar (window->fullscreen_box, GTK_WIDGET (window->titlebar_box));
 
   gtk_container_add (GTK_CONTAINER (window->main_deck), GTK_WIDGET (window->fullscreen_box));
   gtk_container_add (GTK_CONTAINER (window->main_deck), GTK_WIDGET (window->pages_view));
@@ -3864,8 +3718,13 @@ ephy_window_constructed (GObject *object)
   gtk_widget_show (GTK_WIDGET (window->main_deck));
   gtk_widget_show (GTK_WIDGET (window->pages_view));
   gtk_widget_show (GTK_WIDGET (window->fullscreen_box));
+  gtk_widget_show (GTK_WIDGET (window->titlebar_box));
   gtk_widget_show (GTK_WIDGET (box));
-  gtk_widget_show (GTK_WIDGET (window->notebook));
+  gtk_widget_show (GTK_WIDGET (window->tab_view));
+  gtk_widget_show (GTK_WIDGET (window->tab_bar));
+  gtk_widget_show (GTK_WIDGET (window->tab_bar_revealer));
+
+  ephy_tab_view_set_tab_bar (window->tab_view, window->tab_bar);
 
   hdy_deck_set_visible_child (HDY_DECK (window->main_deck), GTK_WIDGET (window->fullscreen_box));
   hdy_deck_set_can_swipe_back (HDY_DECK (window->main_deck), TRUE);
@@ -4013,19 +3872,19 @@ ephy_window_new (void)
 }
 
 /**
- * ephy_window_get_notebook:
+ * ephy_window_get_tab_view:
  * @window: an #EphyWindow
  *
- * Returns the #GtkNotebook used by this window.
+ * Returns the #EphyTabView used by this window.
  *
- * Return value: (transfer none): the @window's #GtkNotebook
+ * Return value: (transfer none): the @window's #EphyTabView
  **/
-GtkWidget *
-ephy_window_get_notebook (EphyWindow *window)
+EphyTabView *
+ephy_window_get_tab_view (EphyWindow *window)
 {
   g_assert (EPHY_IS_WINDOW (window));
 
-  return GTK_WIDGET (window->notebook);
+  return window->tab_view;
 }
 
 /**
@@ -4344,7 +4203,7 @@ ephy_window_check_modified_forms (EphyWindow *window)
   data = g_new0 (ModifiedFormsData, 1);
   data->window = window;
   data->cancellable = g_cancellable_new ();
-  data->embeds_to_check = gtk_notebook_get_n_pages (window->notebook);
+  data->embeds_to_check = ephy_tab_view_get_n_pages (window->tab_view);
 
   tabs = impl_get_children (EPHY_EMBED_CONTAINER (window));
   for (l = tabs; l != NULL; l = l->next) {
@@ -4394,7 +4253,7 @@ ephy_window_close (EphyWindow *window)
   if (!window->force_close &&
       g_settings_get_boolean (EPHY_SETTINGS_MAIN,
                               EPHY_PREFS_WARN_ON_CLOSE_UNSUBMITTED_DATA) &&
-      gtk_notebook_get_n_pages (window->notebook) > 0) {
+      ephy_tab_view_get_n_pages (window->tab_view) > 0) {
     ephy_window_check_modified_forms (window);
     /* stop window close */
     return FALSE;
@@ -4402,7 +4261,7 @@ ephy_window_close (EphyWindow *window)
 
   session = ephy_shell_get_session (ephy_shell_get_default ());
   if (ephy_shell_get_n_windows (ephy_shell_get_default ()) > 1 &&
-      gtk_notebook_get_n_pages (window->notebook) > 1 &&
+      ephy_tab_view_get_n_pages (window->tab_view) > 1 &&
       !ephy_session_is_closing (session) &&
       !confirm_close_with_multiple_tabs (window)) {
     /* stop window close */
@@ -4451,25 +4310,16 @@ int
 ephy_window_get_position_for_new_embed (EphyWindow *window,
                                         EphyEmbed  *embed)
 {
-  GtkWidget *notebook = ephy_window_get_notebook (window);
+  HdyTabView *view = ephy_tab_view_get_tab_view (window->tab_view);
+  HdyTabPage *page;
   int position;
 
   if (embed == window->last_opened_embed)
     return window->last_opened_pos++;
 
-  position = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), GTK_WIDGET (embed)) + 1;
-
-  /* Loop through all pages and skip all pinned tabs */
-  do {
-    GtkWidget *page;
-
-    page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), position);
-    if (!page)
-      break;
-
-    if (!ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), EPHY_EMBED (page)))
-      break;
-  } while (++position < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)));
+  page = hdy_tab_view_get_page (view, GTK_WIDGET (embed));
+  position = hdy_tab_view_get_page_position (view, page) + 1;
+  position = MAX (position, hdy_tab_view_get_n_pinned_pages (view));
 
   if (window->last_opened_embed)
     g_object_remove_weak_pointer (G_OBJECT (window->last_opened_embed), (gpointer 
*)&window->last_opened_embed);
diff --git a/src/ephy-window.h b/src/ephy-window.h
index 378593aa1..caa8d9d78 100644
--- a/src/ephy-window.h
+++ b/src/ephy-window.h
@@ -24,6 +24,7 @@
 #include "ephy-embed.h"
 #include "ephy-embed-event.h"
 #include "ephy-location-controller.h"
+#include "ephy-tab-view.h"
 #include "ephy-web-view.h"
 
 #include <handy.h>
@@ -46,7 +47,7 @@ typedef enum
 
 EphyWindow       *ephy_window_new                 (void);
 
-GtkWidget        *ephy_window_get_notebook        (EphyWindow *window);
+EphyTabView      *ephy_window_get_tab_view        (EphyWindow *window);
 
 void              ephy_window_open_pages_view     (EphyWindow *window);
 void              ephy_window_close_pages_view    (EphyWindow *window);
diff --git a/src/meson.build b/src/meson.build
index 7886d6f14..f3d82d88b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -36,15 +36,13 @@ libephymain_sources = [
   'ephy-location-controller.c',
   'ephy-lockdown.c',
   'ephy-mouse-gesture-controller.c',
-  'ephy-notebook.c',
   'ephy-page-row.c',
   'ephy-pages-button.c',
-  'ephy-pages-popover.c',
   'ephy-pages-view.c',
   'ephy-session.c',
   'ephy-shell.c',
   'ephy-suggestion-model.c',
-  'ephy-tab-label.c',
+  'ephy-tab-view.c',
   'ephy-web-extension-dialog.c',
   'ephy-window.c',
   'popup-commands.c',
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index 28e9a7a0b..72fb8921a 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -41,7 +41,6 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/search-engine-row.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/synced-tabs-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/shortcuts-dialog.ui</file>
-    <file preprocess="xml-stripblanks" compressed="true">gtk/tab-label.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/webapp-additional-urls-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/web-extensions-dialog.ui</file>
   </gresource>
@@ -49,7 +48,6 @@
     <file compressed="true" alias="scalable/actions/ephy-download-symbolic.svg" 
preprocess="xml-stripblanks">ephy-download-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/actions/ephy-bookmarks-symbolic.svg">ephy-bookmarks-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/actions/ephy-bookmark-tag-symbolic.svg">ephy-bookmark-tag-symbolic.svg</file>
-    <file compressed="true" 
alias="scalable/actions/ephy-missing-favicon-symbolic.svg">ephy-missing-favicon-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/actions/ephy-reader-mode-symbolic.svg">ephy-reader-mode-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/status/ephy-audio-muted-symbolic.svg">ephy-audio-muted-symbolic.svg</file>
     <file compressed="true" 
alias="scalable/status/ephy-audio-playing-symbolic.svg">ephy-audio-playing-symbolic.svg</file>
diff --git a/src/resources/gtk/page-row.ui b/src/resources/gtk/page-row.ui
index 17bcb9278..8e95a71a7 100644
--- a/src/resources/gtk/page-row.ui
+++ b/src/resources/gtk/page-row.ui
@@ -4,6 +4,9 @@
   <requires lib="gtk+" version="3.16"/>
   <template class="EphyPageRow" parent="GtkListBoxRow">
     <property name="can_focus">True</property>
+    <style>
+      <class name="narrow"/>
+    </style>
     <child>
       <object class="GtkEventBox">
         <property name="visible">True</property>
@@ -13,6 +16,9 @@
           <object class="GtkBox" id="box">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <property name="height-request">50</property>
+            <property name="margin-start">8</property>
+            <property name="margin-end">4</property>
             <child>
               <object class="GtkStack" id="icon_stack">
                 <property name="visible">True</property>
@@ -32,6 +38,7 @@
                   <object class="GtkSpinner" id="spinner">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="active">True</property>
                   </object>
                   <packing>
                     <property name="position">1</property>
diff --git a/src/resources/themes/_Adwaita-base.scss b/src/resources/themes/_Adwaita-base.scss
index 01a51b9e8..66c62989e 100644
--- a/src/resources/themes/_Adwaita-base.scss
+++ b/src/resources/themes/_Adwaita-base.scss
@@ -85,39 +85,6 @@ $close_button_fg_color: if($variant == 'light', lighten($fg_color, 10%), darken(
   animation-iteration-count: 3;
 }
 
-// Pinned tabs
-.main-notebook {
-  tab {
-    min-width: 16px;
-  }
-
-  header {
-    &.top,
-    &.bottom {
-      tab {
-        border-style: none solid;
-      }
-    }
-
-    &.left,
-    &.right {
-      tab {
-        border-style: solid none;
-      }
-    }
-  }
-}
-
-.tab-attention {
-  $_dot_color: if($variant=='light', $selected_bg_color,
-                                   lighten($selected_bg_color,15%));
-  background-image: -gtk-gradient(radial,
-                                  center center, 0,
-                                  center center, 0.5,
-                                  to($_dot_color),
-                                  to(transparent));
-}
-
 #title-box-container {
   color: gtkalpha(themecolor(theme_fg_color), 0.2);
 
diff --git a/src/resources/themes/_Adwaita-colored-window.scss 
b/src/resources/themes/_Adwaita-colored-window.scss
index 2bf349b78..4047c6894 100644
--- a/src/resources/themes/_Adwaita-colored-window.scss
+++ b/src/resources/themes/_Adwaita-colored-window.scss
@@ -70,59 +70,7 @@ actionbar {
   }
 }
 
-notebook > header {
-  border-color: $borders_color;
-  background-color: $dark_fill;
-
-  &:backdrop {
-    border-color: $backdrop_borders_color;
-    background-color: $backdrop_dark_fill;
-  }
-
-  > tabs > tab {
-    color: $insensitive_fg_color;
-
-    button.flat {
-      &:hover { color: currentColor; }
-      &, &:backdrop { color: gtkalpha(currentColor, 0.3); }
-    }
-
-    &:hover {
-      color: mix($insensitive_fg_color, $fg_color, 50%);
-      border-color: transparentize($borders_color, 0.7);
-      background-color: transparentize($bg_color, 0.8);
-      box-shadow: inset 0 -3px $borders_color;
-    }
-
-    &:backdrop {
-      color: mix($backdrop_fg_color, $backdrop_bg_color, 60%);
-      border-color: transparent;
-      background-color: transparent;
-      box-shadow: none;
-    }
-
-    &:checked {
-      color: $fg_color;
-      border-color: transparentize($borders_color, 0.5);
-      background-color: transparentize($bg_color, 0.5);
-      box-shadow: inset 0 -3px $selected_bg_color;
-
-      &:hover { background-color: transparentize($bg_color, 0.3); }
-    }
-
-    &:backdrop:checked {
-      color: $backdrop_fg_color;
-      border-color: $backdrop_borders_color;
-      background-color: $backdrop_bg_color;
-    }
-  }
-
-  > tabs > arrow:active {
-    @include button(active);
-  }
-}
-
-headerbar, actionbar, notebook > header {
+headerbar, actionbar {
   > * {
     color: $fg_color;
 
@@ -176,3 +124,147 @@ headerbar, actionbar, notebook > header {
     }
   }
 }
+
+/* FIXME this should be inlined after we finalize the colors */
+
+$tab_selected_bg:           darken($bg_color, 6%);
+$tab_selected_bg_backdrop:  $backdrop_bg_color;
+
+@if $variant == 'dark' {
+  $tab_selected_bg:         $bg_color;
+}
+
+$tab_bg:                    darken($tab_selected_bg, 6%);
+$tab_bg_backdrop:           darken($tab_selected_bg_backdrop, 6%);
+
+$tab_bar_bg:                darken($tab_selected_bg, 9%);
+$tab_bar_bg_backdrop:       darken($tab_selected_bg_backdrop, 9%);
+
+$tab_hover_bg:              lighten($tab_bg, 3%);
+$tab_selected_hover_bg:     lighten($tab_selected_bg, 3%);
+
+$tab_needs_attention_color: $selected_bg_color;
+
+@mixin undershoot-gradient($dir) {
+  $color: black;
+
+  @if $variant == 'dark' {
+    background: linear-gradient(to #{$dir},
+                                transparentize($color, .6),
+                                transparentize($color, 1) 20px);
+  }
+  @else {
+    background: linear-gradient(to #{$dir},
+                                transparentize($color, .93),
+                                transparentize($color, 1) 20px);
+  }
+}
+
+@mixin need-attention-gradient($dir) {
+  background: linear-gradient(to #{$dir},
+                              transparentize($tab_needs_attention_color, .3),
+                              transparentize($tab_needs_attention_color, .5) 1px,
+                              transparentize($tab_needs_attention_color, 1) 20px);
+}
+
+@mixin tab-background($color) {
+  background-color: $color;
+
+  .tab-close-button-scrim {
+    &.left {
+      background: linear-gradient(to left, transparentize($color, 1), $color 18px);
+    }
+
+    &.right {
+      background: linear-gradient(to right, transparentize($color, 1), $color 18px);
+    }
+  }
+}
+
+tabbar {
+  .box {
+    background: darken($tab_bg, 3%);
+    border-bottom: 1px solid $alt_borders_color;
+
+    &:backdrop {
+      background-color: darken($tab_bg_backdrop, 3%);
+      border-color: $backdrop_borders_color;
+    }
+  }
+
+  scrolledwindow.pinned {
+    undershoot {
+      border-color: $alt_borders_color;
+    }
+
+    &:backdrop undershoot {
+      border-color: $backdrop_borders_color;
+    }
+
+    tabbox {
+      &:dir(ltr) {
+        box-shadow: inset -1px 0 $alt_borders_color;
+
+        &:backdrop {
+          box-shadow: inset -1px 0 $backdrop_borders_color;
+        }
+      }
+
+      &:dir(rtl) {
+        box-shadow: inset 1px 0 $alt_borders_color;
+
+        &:backdrop {
+          box-shadow: inset 1px 0 $backdrop_borders_color;
+        }
+      }
+    }
+  }
+
+  tab {
+    border-color: $alt_borders_color;
+    background-color: $tab_bg;
+
+    &:checked {
+      background-color: lighten($tab_bg, 6%);
+
+      &:hover {
+        background-color: lighten($tab_bg, 9%);
+      }
+    }
+
+    &:hover {
+      background-color: lighten($tab_bg, 3%);
+    }
+
+    &:backdrop {
+      border-color: $backdrop_borders_color;
+      background-color: $tab_bg_backdrop;
+
+      &:checked {
+        background-color: $backdrop_bg_color;
+      }
+    }
+
+    .tab-close-button,
+    .tab-indicator.clickable {
+      &:hover {
+        background: gtkalpha($fg_color, .15);
+      }
+
+      &:active {
+        background: gtkalpha(black, .2);
+      }
+    }
+  }
+
+  .start-action,
+  .end-action {
+    background: $tab_bg;
+    border-color: $alt_borders_color;
+
+    &:backdrop {
+      border-color: $backdrop_borders_color;
+      background-color: $tab_bg_backdrop;
+    }
+  }
+}
diff --git a/src/resources/themes/elementary.scss b/src/resources/themes/elementary.scss
index 26459a87c..04ca05682 100644
--- a/src/resources/themes/elementary.scss
+++ b/src/resources/themes/elementary.scss
@@ -56,12 +56,6 @@ $variant: 'light';
   }
 }
 
-.main-notebook {
-  tab {
-    min-width: 30px;
-  }
-}
-
 // FIXME: this should be in libdazzle
 dzlsuggestionpopover > revealer > box {
   // Fix background and shadow
diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c
index 162724508..d54126704 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -46,7 +46,7 @@ tabs_handler_query (EphyWebExtension *self,
   g_autoptr (JsonNode) root = NULL;
   EphyShell *shell = ephy_shell_get_default ();
   GtkWindow *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
   gboolean current_window = TRUE;
   gboolean active = TRUE;
 
@@ -66,18 +66,18 @@ tabs_handler_query (EphyWebExtension *self,
 
   if (current_window) {
     window = gtk_application_get_active_window (GTK_APPLICATION (shell));
-    notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+    tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
 
     json_builder_begin_array (builder);
 
     if (active) {
-      GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), gtk_notebook_get_current_page 
(GTK_NOTEBOOK (notebook)));
+      GtkWidget *page = ephy_tab_view_get_selected_page (tab_view);
       EphyWebView *tmp_webview = ephy_embed_get_web_view (EPHY_EMBED (page));
 
       add_web_view_to_json (builder, tmp_webview);
     } else {
-      for (int i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); i++) {
-        GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+      for (int i = 0; i < ephy_tab_view_get_n_pages (tab_view); i++) {
+        GtkWidget *page = ephy_tab_view_get_nth_page (tab_view, i);
         EphyWebView *tmp_webview = ephy_embed_get_web_view (EPHY_EMBED (page));
 
         add_web_view_to_json (builder, tmp_webview);
diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c
index 4208c50d2..c88351bf9 100644
--- a/src/webextension/ephy-web-extension-manager.c
+++ b/src/webextension/ephy-web-extension-manager.c
@@ -288,9 +288,8 @@ ephy_web_extension_manager_update_location_entry (EphyWebExtensionManager *self,
 {
   GtkWidget *title_widget;
   EphyLocationEntry *lentry;
-  GtkWidget *notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
-  int current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
-  GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), current_page);
+  EphyTabView *tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
+  GtkWidget *page = ephy_tab_view_get_selected_page (tab_view);
   EphyWebView *web_view;
 
   if (!page)
@@ -557,14 +556,15 @@ ephy_web_extension_manager_add_web_extension_to_webview (EphyWebExtensionManager
 }
 
 static void
-page_added_cb (GtkNotebook *notebook,
-               GtkWidget   *child,
-               guint        page_num,
-               gpointer     user_data)
+page_attached_cb (EphyTabView *tab_view,
+                  HdyTabPage  *page,
+                  gint         position,
+                  gpointer     user_data)
 {
   EphyWebExtension *web_extension = EPHY_WEB_EXTENSION (user_data);
+  GtkWidget *child = hdy_tab_page_get_child (page);
   EphyWebView *web_view = ephy_embed_get_web_view (EPHY_EMBED (child));
-  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (notebook)));
+  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (tab_view)));
   EphyWebExtensionManager *self = ephy_shell_get_web_extension_manager (ephy_shell_get_default ());
 
 
@@ -752,14 +752,14 @@ ephy_web_extension_manager_add_web_extension_to_window (EphyWebExtensionManager
                                                         EphyWebExtension        *web_extension,
                                                         EphyWindow              *window)
 {
-  GtkWidget *notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+  EphyTabView *tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
 
   if (!ephy_web_extension_manager_is_active (self, web_extension))
     return;
 
   /* Add page actions and add content script */
-  for (int i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); i++) {
-    GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+  for (int i = 0; i < ephy_tab_view_get_n_pages (tab_view); i++) {
+    GtkWidget *page = ephy_tab_view_get_nth_page (tab_view, i);
     EphyWebView *web_view = ephy_embed_get_web_view (EPHY_EMBED (page));
 
     ephy_web_extension_manager_add_web_extension_to_webview (self, web_extension, window, web_view);
@@ -772,7 +772,7 @@ ephy_web_extension_manager_add_web_extension_to_window (EphyWebExtensionManager
   }
 
   ephy_web_extension_manager_update_location_entry (self, window);
-  g_signal_connect_object (notebook, "page-added", G_CALLBACK (page_added_cb), web_extension, 0);
+  g_signal_connect_object (tab_view, "page-attached", G_CALLBACK (page_attached_cb), web_extension, 0);
 }
 
 static gboolean
@@ -809,14 +809,14 @@ ephy_web_extension_manager_remove_web_extension_from_window (EphyWebExtensionMan
                                                              EphyWebExtension        *web_extension,
                                                              EphyWindow              *window)
 {
-  GtkWidget *notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+  EphyTabView *tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
   GtkWidget *browser_action_widget;
 
   if (ephy_web_extension_manager_is_active (self, web_extension))
     return;
 
-  for (int i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)); i++) {
-    GtkWidget *page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+  for (int i = 0; i < ephy_tab_view_get_n_pages (tab_view); i++) {
+    GtkWidget *page = ephy_tab_view_get_nth_page (tab_view, i);
     EphyWebView *web_view = ephy_embed_get_web_view (EPHY_EMBED (page));
 
     ephy_web_extension_manager_remove_web_extension_from_webview (self, web_extension, window, web_view);
@@ -829,7 +829,7 @@ ephy_web_extension_manager_remove_web_extension_from_window (EphyWebExtensionMan
 
   ephy_web_extension_manager_update_location_entry (self, window);
 
-  g_signal_handlers_disconnect_by_data (notebook, web_extension);
+  g_signal_handlers_disconnect_by_data (tab_view, web_extension);
 }
 
 gboolean
diff --git a/src/window-commands.c b/src/window-commands.c
index daa9135dd..3feb7e214 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -46,13 +46,13 @@
 #include "ephy-history-dialog.h"
 #include "ephy-link.h"
 #include "ephy-location-entry.h"
-#include "ephy-notebook.h"
 #include "ephy-password-import.h"
 #include "ephy-prefs.h"
 #include "ephy-session.h"
 #include "ephy-settings.h"
 #include "ephy-shell.h"
 #include "ephy-string.h"
+#include "ephy-tab-view.h"
 #include "ephy-view-source-handler.h"
 #include "ephy-web-app-utils.h"
 #include "ephy-web-extension-dialog.h"
@@ -1180,17 +1180,14 @@ window_cmd_stop (GSimpleAction *action,
 static void
 check_tab_has_modified_forms_and_reload_cb (EphyWebView  *view,
                                             GAsyncResult *result,
-                                            gpointer      user_data)
+                                            EphyEmbed    *embed)
 {
-  EphyWindow *window = EPHY_WINDOW (user_data);
-  EphyEmbed *embed;
+  EphyWindow *window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
   GtkWidget *dialog;
   GtkWidget *button;
   gboolean has_modified_forms;
   int response = GTK_RESPONSE_ACCEPT;
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
-
   has_modified_forms = ephy_web_view_has_modified_forms_finish (view, result, NULL);
   if (has_modified_forms) {
     dialog = gtk_message_dialog_new (GTK_WINDOW (window),
@@ -1220,7 +1217,7 @@ check_tab_has_modified_forms_and_reload_cb (EphyWebView  *view,
     webkit_web_view_reload (view);
   }
 
-  g_object_unref (window);
+  g_object_unref (embed);
 }
 
 void
@@ -1231,13 +1228,13 @@ window_cmd_reload (GSimpleAction *action,
   EphyWindow *window = EPHY_WINDOW (user_data);
   EphyEmbed *embed;
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+  embed = EPHY_EMBED (ephy_tab_view_get_current_page (ephy_window_get_tab_view (window)));
   g_assert (embed != NULL);
 
   ephy_web_view_has_modified_forms (ephy_embed_get_web_view (embed),
                                     NULL,
                                     (GAsyncReadyCallback)check_tab_has_modified_forms_and_reload_cb,
-                                    g_object_ref (window));
+                                    g_object_ref (embed));
 }
 
 void
@@ -2657,80 +2654,21 @@ window_cmd_change_fullscreen_state (GSimpleAction *action,
   g_simple_action_set_state (action, g_variant_new_boolean (active));
 }
 
-void
-window_cmd_tabs_previous (GSimpleAction *action,
-                          GVariant      *variant,
-                          gpointer       user_data)
-{
-  GtkWidget *nb;
-
-  nb = ephy_window_get_notebook (EPHY_WINDOW (user_data));
-  g_assert (nb != NULL);
-
-  ephy_notebook_prev_page (EPHY_NOTEBOOK (nb));
-}
-
-void
-window_cmd_tabs_next (GSimpleAction *action,
-                      GVariant      *variant,
-                      gpointer       user_data)
-{
-  GtkWidget *nb;
-
-  nb = ephy_window_get_notebook (EPHY_WINDOW (user_data));
-  g_assert (nb != NULL);
-
-  ephy_notebook_next_page (EPHY_NOTEBOOK (nb));
-}
-
-void
-window_cmd_tabs_move_left (GSimpleAction *action,
-                           GVariant      *variant,
-                           gpointer       user_data)
-{
-  GtkWidget *child;
-  GtkNotebook *notebook;
-  int page;
-
-  notebook = GTK_NOTEBOOK (ephy_window_get_notebook (EPHY_WINDOW (user_data)));
-  page = gtk_notebook_get_current_page (notebook);
-  if (page < 1)
-    return;
-
-  child = gtk_notebook_get_nth_page (notebook, page);
-  gtk_notebook_reorder_child (notebook, child, page - 1);
-}
-
-void window_cmd_tabs_move_right (GSimpleAction *action,
-                                 GVariant      *variant,
-                                 gpointer       user_data)
-{
-  GtkWidget *child;
-  GtkNotebook *notebook;
-  int page, n_pages;
-
-  notebook = GTK_NOTEBOOK (ephy_window_get_notebook (EPHY_WINDOW (user_data)));
-  page = gtk_notebook_get_current_page (notebook);
-  n_pages = gtk_notebook_get_n_pages (notebook) - 1;
-  if (page > n_pages - 1)
-    return;
-
-  child = gtk_notebook_get_nth_page (notebook, page);
-  gtk_notebook_reorder_child (notebook, child, page + 1);
-}
-
 void
 window_cmd_tabs_duplicate (GSimpleAction *action,
                            GVariant      *variant,
                            gpointer       user_data)
 {
+  EphyTabView *tab_view;
   EphyEmbed *embed, *new_embed;
   WebKitWebView *view, *new_view;
   WebKitWebViewSessionState *session_state;
   WebKitBackForwardList *bf_list;
   WebKitBackForwardListItem *item;
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (user_data));
+  tab_view = ephy_window_get_tab_view (EPHY_WINDOW (user_data));
+  embed = EPHY_EMBED (ephy_tab_view_get_current_page (tab_view));
+
   view = WEBKIT_WEB_VIEW (ephy_embed_get_web_view (embed));
   session_state = webkit_web_view_get_session_state (view);
 
@@ -2758,25 +2696,17 @@ window_cmd_tabs_close (GSimpleAction *action,
                        gpointer       user_data)
 {
   EphyWindow *window = user_data;
-  GtkWidget *notebook;
-  EphyEmbed *embed;
-  gboolean pinned;
+  EphyTabView *tab_view;
 
-  notebook = ephy_window_get_notebook (window);
+  tab_view = ephy_window_get_tab_view (window);
 
   if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                               EPHY_PREFS_LOCKDOWN_QUIT) &&
-      gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) == 1) {
+      ephy_tab_view_get_n_pages (tab_view) <= 1) {
     return;
   }
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
-  g_assert (embed != NULL);
-
-  pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed);
-
-  if (!pinned)
-    g_signal_emit_by_name (notebook, "tab-close-request", embed);
+  ephy_tab_view_close_selected (tab_view);
 }
 
 void
@@ -2785,27 +2715,8 @@ window_cmd_tabs_close_left (GSimpleAction *action,
                             gpointer       user_data)
 {
   EphyWindow *window = user_data;
-  GtkWidget *notebook;
-  EphyEmbed *embed;
-  int current_page_no;
-  GSList *pages_to_close = NULL;
-
-  notebook = ephy_window_get_notebook (window);
-  current_page_no = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
 
-  for (int i = 0; i < current_page_no; i++) {
-    embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
-
-    if (!ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed))
-      pages_to_close = g_slist_prepend (pages_to_close, embed);
-  }
-
-  for (GSList *l = pages_to_close; l != NULL; l = l->next) {
-    g_assert (l->data != NULL);
-    g_signal_emit_by_name (GTK_NOTEBOOK (notebook), "tab-close-request", l->data);
-  }
-
-  g_slist_free (pages_to_close);
+  ephy_tab_view_close_left (ephy_window_get_tab_view (window));
 }
 
 void
@@ -2814,28 +2725,8 @@ window_cmd_tabs_close_right (GSimpleAction *action,
                              gpointer       user_data)
 {
   EphyWindow *window = user_data;
-  GtkWidget *notebook;
-  EphyEmbed *embed;
-  int n_pages, current_page_no;
-  GSList *pages_to_close = NULL;
 
-  notebook = ephy_window_get_notebook (window);
-  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
-  current_page_no = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
-
-  for (int i = current_page_no + 1; i < n_pages; i++) {
-    embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
-
-    if (!ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed))
-      pages_to_close = g_slist_prepend (pages_to_close, embed);
-  }
-
-  for (GSList *l = pages_to_close; l != NULL; l = l->next) {
-    g_assert (l->data != NULL);
-    g_signal_emit_by_name (GTK_NOTEBOOK (notebook), "tab-close-request", l->data);
-  }
-
-  g_slist_free (pages_to_close);
+  ephy_tab_view_close_right (ephy_window_get_tab_view (window));
 }
 
 void
@@ -2844,30 +2735,18 @@ window_cmd_tabs_close_others (GSimpleAction *action,
                               gpointer       user_data)
 {
   EphyWindow *window = user_data;
-  GtkWidget *notebook;
-  EphyEmbed *embed;
-  int n_pages, current_page;
-  GSList *pages_to_close = NULL;
 
-  notebook = ephy_window_get_notebook (window);
-  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
-  current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
-
-  for (int i = 0; i < n_pages; i++) {
-    if (i != current_page) {
-      embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
-
-      if (!ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed))
-        pages_to_close = g_slist_prepend (pages_to_close, embed);
-    }
-  }
+  ephy_tab_view_close_other (ephy_window_get_tab_view (window));
+}
 
-  for (GSList *l = pages_to_close; l != NULL; l = l->next) {
-    g_assert (l->data != NULL);
-    g_signal_emit_by_name (GTK_NOTEBOOK (notebook), "tab-close-request", l->data);
-  }
+static void
+reload_cb (GtkWidget *widget,
+           gpointer   user_data)
+{
+  EphyEmbed *embed = EPHY_EMBED (widget);
+  WebKitWebView *view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
 
-  g_slist_free (pages_to_close);
+  webkit_web_view_reload (view);
 }
 
 void
@@ -2876,45 +2755,10 @@ window_cmd_tabs_reload_all_tabs (GSimpleAction *action,
                                  gpointer       user_data)
 {
   EphyWindow *window = user_data;
-  EphyEmbed *embed;
-  WebKitWebView *view;
-  GtkWidget *notebook;
-  int n_pages;
-
-  notebook = ephy_window_get_notebook (window);
-  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
-
-  for (int i = 0; i < n_pages; i++) {
-    embed = EPHY_EMBED (gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i));
-
-    view = EPHY_GET_WEBKIT_WEB_VIEW_FROM_EMBED (embed);
-    webkit_web_view_reload (view);
-  }
-}
-
-void
-window_cmd_show_tab (GSimpleAction *action,
-                     GVariant      *parameter,
-                     gpointer       user_data)
-{
-  EphyWindow *window = EPHY_WINDOW (user_data);
-  GtkWidget *notebook;
-  guint32 tab_num;
-
-  g_assert (g_variant_is_of_type (parameter, G_VARIANT_TYPE_UINT32));
-  tab_num = g_variant_get_uint32 (parameter);
-
-  notebook = ephy_window_get_notebook (window);
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), tab_num);
-  g_simple_action_set_state (action, parameter);
-}
 
-void
-window_cmd_change_show_tab_state (GSimpleAction *action,
-                                  GVariant      *parameter,
-                                  gpointer       user_data)
-{
-  /* This page intentionally left blank. */
+  ephy_tab_view_foreach (ephy_window_get_tab_view (window),
+                         reload_cb,
+                         NULL);
 }
 
 void
@@ -3029,15 +2873,8 @@ window_cmd_tabs_pin (GSimpleAction *action,
                      gpointer       user_data)
 {
   EphyWindow *window = EPHY_WINDOW (user_data);
-  EphyEmbed *embed;
-  EphyNotebook *notebook;
 
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
-  g_assert (embed != NULL);
-
-  notebook = EPHY_NOTEBOOK (ephy_window_get_notebook (window));
-
-  ephy_notebook_tab_set_pinned (notebook, GTK_WIDGET (embed), TRUE);
+  ephy_tab_view_pin (ephy_window_get_tab_view (window));
 }
 
 void
@@ -3046,15 +2883,8 @@ window_cmd_tabs_unpin (GSimpleAction *action,
                        gpointer       user_data)
 {
   EphyWindow *window = EPHY_WINDOW (user_data);
-  EphyEmbed *embed;
-  EphyNotebook *notebook;
-
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
-  g_assert (embed != NULL);
 
-  notebook = EPHY_NOTEBOOK (ephy_window_get_notebook (window));
-
-  ephy_notebook_tab_set_pinned (notebook, GTK_WIDGET (embed), FALSE);
+  ephy_tab_view_unpin (ephy_window_get_tab_view (window));
 }
 
 void
@@ -3067,13 +2897,16 @@ window_cmd_change_tabs_mute_state (GSimpleAction *action,
   EphyWebView *view;
   gboolean mute;
 
-  mute = g_variant_get_boolean (state);
-
-  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+  embed = EPHY_EMBED (ephy_tab_view_get_current_page (ephy_window_get_tab_view (window)));
   g_assert (embed != NULL);
 
   view = ephy_embed_get_web_view (embed);
 
+  if (!webkit_web_view_is_playing_audio (WEBKIT_WEB_VIEW (view)))
+    return;
+
+  mute = !webkit_web_view_get_is_muted (WEBKIT_WEB_VIEW (view));
+
   webkit_web_view_set_is_muted (WEBKIT_WEB_VIEW (view), mute);
 
   g_simple_action_set_state (action, g_variant_new_boolean (mute));
diff --git a/src/window-commands.h b/src/window-commands.h
index 25a3b1da3..2c56d9288 100644
--- a/src/window-commands.h
+++ b/src/window-commands.h
@@ -179,18 +179,6 @@ void window_cmd_change_browse_with_caret_state  (GSimpleAction *action,
 void window_cmd_change_fullscreen_state         (GSimpleAction *action,
                                                  GVariant      *state,
                                                  gpointer       user_data);
-void window_cmd_tabs_previous                   (GSimpleAction *action,
-                                                 GVariant      *state,
-                                                 gpointer       user_data);
-void window_cmd_tabs_next                       (GSimpleAction *action,
-                                                 GVariant      *state,
-                                                 gpointer       user_data);
-void window_cmd_tabs_move_left                  (GSimpleAction *action,
-                                                 GVariant      *state,
-                                                 gpointer       user_data);
-void window_cmd_tabs_move_right                 (GSimpleAction *action,
-                                                 GVariant      *state,
-                                                 gpointer       user_data);
 void window_cmd_tabs_duplicate                  (GSimpleAction *action,
                                                  GVariant      *state,
                                                  gpointer       user_data);
diff --git a/tests/ephy-shell-test.c b/tests/ephy-shell-test.c
index c47035da9..6d3255cad 100644
--- a/tests/ephy-shell-test.c
+++ b/tests/ephy-shell-test.c
@@ -168,19 +168,12 @@ test_ephy_shell_tab_load (void)
   gtk_widget_destroy (window);
 }
 
-static int
-get_notebook_page_num (GtkWidget *notebook,
-                       EphyEmbed *embed)
-{
-  return gtk_notebook_page_num (GTK_NOTEBOOK (notebook), GTK_WIDGET (embed));
-}
-
 static void
 test_ephy_shell_tab_append (void)
 {
   EphyShell *ephy_shell;
   GtkWidget *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
 
   EphyEmbed *embed1;
   EphyEmbed *embed2;
@@ -190,37 +183,37 @@ test_ephy_shell_tab_append (void)
 
   ephy_shell = ephy_shell_get_default ();
   window = GTK_WIDGET (ephy_window_new ());
-  notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+  tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
 
   embed1 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), NULL,
                                EPHY_NEW_TAB_DONT_SHOW_WINDOW);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed1), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed1), ==, 0);
 
   embed2 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), embed1,
                                EPHY_NEW_TAB_DONT_SHOW_WINDOW);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed1), ==, 0);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed2), ==, 1);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed1), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed2), ==, 1);
 
   embed3 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), embed1,
                                EPHY_NEW_TAB_DONT_SHOW_WINDOW | EPHY_NEW_TAB_APPEND_AFTER);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed1), ==, 0);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed3), ==, 1);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed2), ==, 2);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed1), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed3), ==, 1);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed2), ==, 2);
 
   embed4 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), embed1,
                                EPHY_NEW_TAB_DONT_SHOW_WINDOW | EPHY_NEW_TAB_APPEND_LAST);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed1), ==, 0);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed3), ==, 1);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed2), ==, 2);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed4), ==, 3);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed1), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed3), ==, 1);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed2), ==, 2);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed4), ==, 3);
 
   embed5 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), embed3,
                                EPHY_NEW_TAB_DONT_SHOW_WINDOW | EPHY_NEW_TAB_APPEND_AFTER);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed1), ==, 0);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed3), ==, 1);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed5), ==, 2);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed2), ==, 3);
-  g_assert_cmpint (get_notebook_page_num (notebook, embed4), ==, 4);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed1), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed3), ==, 1);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed5), ==, 2);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed2), ==, 3);
+  g_assert_cmpint (ephy_tab_view_get_page_index (tab_view, embed4), ==, 4);
 
   gtk_widget_destroy (window);
 }
@@ -231,7 +224,7 @@ test_ephy_shell_tab_from_external (void)
 {
   EphyShell *ephy_shell;
   GtkWidget *window;
-  GtkWidget *notebook;
+  EphyTabView *tab_view;
   GMainLoop *loop;
 
   EphyEmbed *embed;
@@ -247,7 +240,7 @@ test_ephy_shell_tab_from_external (void)
   embed = ephy_shell_new_tab (ephy_shell, NULL, NULL, "about:epiphany",
                               EPHY_NEW_TAB_DONT_SHOW_WINDOW | EPHY_NEW_TAB_OPEN_PAGE);
   window = gtk_widget_get_toplevel (GTK_WIDGET (embed));
-  notebook = ephy_window_get_notebook (EPHY_WINDOW (window));
+  tab_view = ephy_window_get_tab_view (EPHY_WINDOW (window));
 
   /* This embed should be used in load-from-external. */
   embed2 = ephy_shell_new_tab (ephy_shell, EPHY_WINDOW (window), NULL, NULL,
@@ -268,7 +261,7 @@ test_ephy_shell_tab_from_external (void)
 
   /* This one should fail, because the active embed is not @embed2. */
   ephy_test_utils_check_ephy_embed_address (embed2, "ephy-about:overview");
-  g_assert_cmpint (gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)), ==, 0);
+  g_assert_cmpint (ephy_tab_view_get_selected_index (tab_view), ==, 0);
 
   loop = ephy_test_utils_setup_ensure_web_views_are_loaded ();
 
@@ -281,11 +274,11 @@ test_ephy_shell_tab_from_external (void)
   ephy_test_utils_check_ephy_embed_address (embed2, "ephy-about:overview");
   ephy_test_utils_check_ephy_embed_address (embed4, "ephy-about:applications");
 
-  gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 1);
+  ephy_tab_view_select_nth_page (tab_view, 1);
 
   /* This should work */
   ephy_test_utils_check_ephy_embed_address (embed2, "ephy-about:overview");
-  g_assert_cmpint (gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook)), ==, 1);
+  g_assert_cmpint (ephy_tab_view_get_selected_index (tab_view), ==, 1);
 
   loop = ephy_test_utils_setup_wait_until_load_is_committed (ephy_embed_get_web_view (embed2));
 


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