[libadwaita/wip/exalm/tab-overview: 15/15] aaa a# Please enter the commit message for your changes. Lines starting




commit 2eac6d2f5af9f5325fe9ce297cb46ffa684018f2
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Sat Aug 28 20:52:33 2021 +0500

    aaa a# Please enter the commit message for your changes. Lines starting

 examples/adw-tab-view-demo-window.c   |  15 +-
 examples/adw-tab-view-demo-window.ui  |  75 ++--
 src/adw-tab-box.c                     |   1 +
 src/adw-tab-grid-private.h            |  30 ++
 src/adw-tab-grid.c                    |  73 ++++
 src/adw-tab-item-private.h            |  11 +
 src/adw-tab-item.c                    | 103 +++++
 src/adw-tab-list-base.c               |  23 +-
 src/adw-tab-overview.c                | 750 ++++++++++++++++++++++++++++++++++
 src/adw-tab-overview.h                |  53 +++
 src/adw-tab-overview.ui               |  55 +++
 src/adw-tab-thumbnail-private.h       |  25 ++
 src/adw-tab-thumbnail.c               | 419 +++++++++++++++++++
 src/adw-tab-thumbnail.ui              |  31 ++
 src/adw-tab.c                         |  84 +---
 src/adwaita.gresources.xml            |   3 +
 src/adwaita.h                         |   1 +
 src/glsl/tab-overview.glsl            |  18 +
 src/meson.build                       |   5 +
 src/stylesheet/widgets/_tab-view.scss |  14 +
 20 files changed, 1678 insertions(+), 111 deletions(-)
---
diff --git a/examples/adw-tab-view-demo-window.c b/examples/adw-tab-view-demo-window.c
index 2de60b79..3bb12adb 100644
--- a/examples/adw-tab-view-demo-window.c
+++ b/examples/adw-tab-view-demo-window.c
@@ -7,6 +7,7 @@ struct _AdwTabViewDemoWindow
   AdwWindow parent_instance;
   AdwTabView *view;
   AdwTabBar *tab_bar;
+  AdwTabOverview *tab_overview;
 
   GActionMap *tab_action_group;
 
@@ -74,21 +75,23 @@ add_page (AdwTabViewDemoWindow *self,
           const char           *title,
           GIcon                *icon)
 {
-  GtkWidget *content;
+  GtkWidget *content, *entry;
   AdwTabPage *page;
 
-  content = g_object_new (GTK_TYPE_ENTRY,
+  entry = g_object_new (GTK_TYPE_ENTRY,
                           "text", title,
                           "halign", GTK_ALIGN_CENTER,
                           "valign", GTK_ALIGN_CENTER,
                           NULL);
 
+  content = g_object_new (ADW_TYPE_BIN, "child", entry, NULL);
+
   page = adw_tab_view_add_page (self->view, GTK_WIDGET (content), parent);
 
-  g_object_bind_property (content, "text",
+  g_object_bind_property (entry, "text",
                           page, "title",
                           G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-  g_object_bind_property_full (content, "text",
+  g_object_bind_property_full (entry, "text",
                                page, "tooltip",
                                G_BINDING_SYNC_CREATE,
                                text_to_tooltip, NULL,
@@ -482,6 +485,7 @@ adw_tab_view_demo_window_class_init (AdwTabViewDemoWindowClass *klass)
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Adwaita/Demo/ui/adw-tab-view-demo-window.ui");
   gtk_widget_class_bind_template_child (widget_class, AdwTabViewDemoWindow, view);
   gtk_widget_class_bind_template_child (widget_class, AdwTabViewDemoWindow, tab_bar);
+  gtk_widget_class_bind_template_child (widget_class, AdwTabViewDemoWindow, tab_overview);
   gtk_widget_class_bind_template_callback (widget_class, page_detached_cb);
   gtk_widget_class_bind_template_callback (widget_class, setup_menu_cb);
   gtk_widget_class_bind_template_callback (widget_class, create_window_cb);
@@ -522,6 +526,9 @@ adw_tab_view_demo_window_init (AdwTabViewDemoWindow *self)
   adw_tab_bar_setup_extra_drop_target (self->tab_bar,
                                        GDK_ACTION_COPY,
                                        (GType[1]) { G_TYPE_STRING }, 1);
+  adw_tab_overview_setup_extra_drop_target (self->tab_overview,
+                                            GDK_ACTION_COPY,
+                                            (GType[1]) { G_TYPE_STRING }, 1);
 }
 
 AdwTabViewDemoWindow *
diff --git a/examples/adw-tab-view-demo-window.ui b/examples/adw-tab-view-demo-window.ui
index 1da1777a..330db47a 100644
--- a/examples/adw-tab-view-demo-window.ui
+++ b/examples/adw-tab-view-demo-window.ui
@@ -7,41 +7,56 @@
     <property name="default-width">800</property>
     <property name="default-height">600</property>
     <child>
-      <object class="GtkBox">
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkHeaderBar">
-            <child type="start">
-              <object class="GtkButton">
-                <property name="action-name">win.tab-new</property>
-                <property name="icon-name">tab-new-symbolic</property>
+      <object class="AdwTabOverview" id="tab_overview">
+        <property name="view">view</property>
+        <signal name="extra-drag-drop" handler="extra_drag_drop_cb" swapped="true"/>
+        <property name="child">
+          <object class="GtkBox">
+            <style>
+              <class name="background"/>
+            </style>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkHeaderBar">
+                <child type="start">
+                  <object class="GtkButton">
+                    <property name="action-name">win.tab-new</property>
+                    <property name="icon-name">tab-new-symbolic</property>
+                  </object>
+                </child>
+                <child type="end">
+                  <object class="GtkMenuButton">
+                    <property name="menu-model">primary_menu</property>
+                    <property name="icon-name">open-menu-symbolic</property>
+                  </object>
+                </child>
+                <child type="end">
+                  <object class="AdwTabButton">
+                    <property name="view">view</property>
+                    <property name="action-name">overview.open</property>
+                  </object>
+                </child>
               </object>
             </child>
-            <child type="end">
-              <object class="GtkMenuButton">
-                <property name="menu-model">primary_menu</property>
-                <property name="icon-name">open-menu-symbolic</property>
+            <child>
+              <object class="AdwTabBar" id="tab_bar">
+                <property name="view">view</property>
+                <signal name="extra-drag-drop" handler="extra_drag_drop_cb" swapped="true"/>
+              </object>
+            </child>
+            <child>
+              <object class="AdwTabView" id="view">
+                <property name="vexpand">True</property>
+                <property name="menu-model">tab_menu</property>
+                <property name="shortcut-widget">AdwTabViewDemoWindow</property>
+                <signal name="page-detached" handler="page_detached_cb" swapped="true"/>
+                <signal name="setup-menu" handler="setup_menu_cb" swapped="true"/>
+                <signal name="create-window" handler="create_window_cb" swapped="true"/>
+                <signal name="indicator-activated" handler="indicator_activated_cb" swapped="true"/>
               </object>
             </child>
           </object>
-        </child>
-        <child>
-          <object class="AdwTabBar" id="tab_bar">
-            <property name="view">view</property>
-            <signal name="extra-drag-drop" handler="extra_drag_drop_cb" swapped="true"/>
-          </object>
-        </child>
-        <child>
-          <object class="AdwTabView" id="view">
-            <property name="vexpand">True</property>
-            <property name="menu-model">tab_menu</property>
-            <property name="shortcut-widget">AdwTabViewDemoWindow</property>
-            <signal name="page-detached" handler="page_detached_cb" swapped="true"/>
-            <signal name="setup-menu" handler="setup_menu_cb" swapped="true"/>
-            <signal name="create-window" handler="create_window_cb" swapped="true"/>
-            <signal name="indicator-activated" handler="indicator_activated_cb" swapped="true"/>
-          </object>
-        </child>
+        </property>
       </object>
     </child>
   </template>
diff --git a/src/adw-tab-box.c b/src/adw-tab-box.c
index 3147d18d..5d9e6d4c 100644
--- a/src/adw-tab-box.c
+++ b/src/adw-tab-box.c
@@ -55,6 +55,7 @@ adw_tab_box_class_init (AdwTabBoxClass *klass)
 static void
 adw_tab_box_init (AdwTabBox *self)
 {
+  gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
 }
 
 void
diff --git a/src/adw-tab-grid-private.h b/src/adw-tab-grid-private.h
new file mode 100644
index 00000000..7d6c2d80
--- /dev/null
+++ b/src/adw-tab-grid-private.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-tab-list-base-private.h"
+
+#include "adw-tab-overview.h"
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_TAB_GRID (adw_tab_grid_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwTabGrid, adw_tab_grid, ADW, TAB_GRID, AdwTabListBase)
+
+void adw_tab_grid_set_tab_overview (AdwTabGrid     *self,
+                                    AdwTabOverview *tab_overview);
+
+GtkPicture *adw_tab_grid_get_transition_picture (AdwTabGrid *self);
+
+G_END_DECLS
diff --git a/src/adw-tab-grid.c b/src/adw-tab-grid.c
new file mode 100644
index 00000000..dfd60538
--- /dev/null
+++ b/src/adw-tab-grid.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+
+#include "adw-tab-grid-private.h"
+
+#include "adw-tab-thumbnail-private.h"
+
+struct _AdwTabGrid
+{
+  AdwTabListBase parent_instance;
+
+  AdwTabOverview *tab_overview;
+};
+
+G_DEFINE_TYPE (AdwTabGrid, adw_tab_grid, ADW_TYPE_TAB_LIST_BASE)
+
+static gboolean
+adw_tab_grid_tabs_have_visible_focus (AdwTabListBase *base)
+{
+  return FALSE;
+}
+
+static void
+adw_tab_grid_activate_item (AdwTabListBase *base,
+                            AdwTabItem     *item)
+{
+  AdwTabGrid *self = ADW_TAB_GRID (base);
+
+  adw_tab_overview_close (self->tab_overview);
+}
+
+static void
+adw_tab_grid_class_init (AdwTabGridClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  AdwTabListBaseClass *base_class = ADW_TAB_LIST_BASE_CLASS (klass);
+
+  base_class->tabs_have_visible_focus = adw_tab_grid_tabs_have_visible_focus;
+  base_class->activate_item = adw_tab_grid_activate_item;
+
+  base_class->item_type = ADW_TYPE_TAB_THUMBNAIL;
+
+  gtk_widget_class_set_css_name (widget_class, "tabgrid");
+}
+
+static void
+adw_tab_grid_init (AdwTabGrid *self)
+{
+  adw_tab_list_base_set_expand_tabs (ADW_TAB_LIST_BASE (self), FALSE);
+}
+
+void
+adw_tab_grid_set_tab_overview (AdwTabGrid     *self,
+                               AdwTabOverview *tab_overview)
+{
+  self->tab_overview = tab_overview;
+}
+
+GtkPicture *
+adw_tab_grid_get_transition_picture (AdwTabGrid *self)
+{
+  GtkWidget *item = adw_tab_list_base_get_selected_item (ADW_TAB_LIST_BASE (self));
+  AdwTabThumbnail *thumbnail = ADW_TAB_THUMBNAIL (item);
+
+  return adw_tab_thumbnail_get_picture (thumbnail);
+}
diff --git a/src/adw-tab-item-private.h b/src/adw-tab-item-private.h
index ce085b6e..aca59674 100644
--- a/src/adw-tab-item-private.h
+++ b/src/adw-tab-item-private.h
@@ -27,6 +27,13 @@ struct _AdwTabItemClass
 
   void (*connect_page)    (AdwTabItem *self);
   void (*disconnect_page) (AdwTabItem *self);
+
+  int  (*measure_contents)  (AdwTabItem     *self,
+                             GtkOrientation  orientation,
+                             int             for_size);
+  void (*allocate_contents) (AdwTabItem     *self,
+                             GtkAllocation  *alloc,
+                             int             baseline);
 };
 
 AdwTabView *adw_tab_item_get_view (AdwTabItem *self);
@@ -41,6 +48,10 @@ int  adw_tab_item_get_display_width (AdwTabItem *self);
 void adw_tab_item_set_display_width (AdwTabItem *self,
                                      int         width);
 
+int  adw_tab_item_get_display_height (AdwTabItem *self);
+void adw_tab_item_set_display_height (AdwTabItem *self,
+                                      int         height);
+
 gboolean adw_tab_item_get_hovering (AdwTabItem *self);
 
 gboolean adw_tab_item_get_dragging (AdwTabItem *self);
diff --git a/src/adw-tab-item.c b/src/adw-tab-item.c
index ba02ffec..bd934995 100644
--- a/src/adw-tab-item.c
+++ b/src/adw-tab-item.c
@@ -19,6 +19,7 @@ typedef struct {
   gboolean dragging;
   gboolean fully_visible;
   int display_width;
+  int display_height;
   gboolean inverted;
 } AdwTabItemPrivate;
 
@@ -33,6 +34,7 @@ enum {
   PROP_DRAGGING,
   PROP_FULLY_VISIBLE,
   PROP_DISPLAY_WIDTH,
+  PROP_DISPLAY_HEIGHT,
   PROP_INVERTED,
   LAST_PROP
 };
@@ -149,6 +151,10 @@ adw_tab_item_get_property (GObject    *object,
     g_value_set_int (value, adw_tab_item_get_display_width (self));
     break;
 
+  case PROP_DISPLAY_HEIGHT:
+    g_value_set_int (value, adw_tab_item_get_display_height (self));
+    break;
+
   case PROP_INVERTED:
     g_value_set_boolean (value, adw_tab_item_get_inverted (self));
     break;
@@ -192,6 +198,10 @@ adw_tab_item_set_property (GObject      *object,
     adw_tab_item_set_display_width (self, g_value_get_int (value));
     break;
 
+  case PROP_DISPLAY_HEIGHT:
+    adw_tab_item_set_display_height (self, g_value_get_int (value));
+    break;
+
   case PROP_INVERTED:
     adw_tab_item_set_inverted (self, g_value_get_boolean (value));
     break;
@@ -211,6 +221,56 @@ adw_tab_item_dispose (GObject *object)
   G_OBJECT_CLASS (adw_tab_item_parent_class)->dispose (object);
 }
 
+static void
+adw_tab_item_measure (GtkWidget      *widget,
+                      GtkOrientation  orientation,
+                      int             for_size,
+                      int            *minimum,
+                      int            *natural,
+                      int            *minimum_baseline,
+                      int            *natural_baseline)
+{
+  AdwTabItem *self = ADW_TAB_ITEM (widget);
+
+  if (minimum)
+    *minimum = 0;
+  if (natural)
+    *natural = ADW_TAB_ITEM_GET_CLASS (self)->measure_contents (self, orientation, for_size);
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+  if (natural_baseline)
+    *natural_baseline = -1;
+}
+
+static void
+adw_tab_item_size_allocate (GtkWidget *widget,
+                            int        width,
+                            int        height,
+                            int        baseline)
+{
+  AdwTabItem *self = ADW_TAB_ITEM (widget);
+  AdwTabItemPrivate *priv = adw_tab_item_get_instance_private (self);
+  GtkAllocation child_alloc;
+  int allocated_width, allocated_height;
+  int width_diff, height_diff;
+
+  if (!gtk_widget_get_first_child (widget))
+    return;
+
+  allocated_width = gtk_widget_get_allocated_width (widget);
+  allocated_height = gtk_widget_get_allocated_height (widget);
+
+  width_diff = MAX (0, priv->display_width - allocated_width);
+  height_diff = MAX (0, priv->display_height - allocated_height);
+
+  child_alloc.x = -width_diff / 2;
+  child_alloc.y = -height_diff / 2;
+  child_alloc.height = height + height_diff;
+  child_alloc.width = width + width_diff;
+
+  ADW_TAB_ITEM_GET_CLASS (self)->allocate_contents (self, &child_alloc, baseline);
+}
+
 static void
 adw_tab_item_class_init (AdwTabItemClass *klass)
 {
@@ -221,6 +281,9 @@ adw_tab_item_class_init (AdwTabItemClass *klass)
   object_class->get_property = adw_tab_item_get_property;
   object_class->set_property = adw_tab_item_set_property;
 
+  widget_class->measure = adw_tab_item_measure;
+  widget_class->size_allocate = adw_tab_item_size_allocate;
+
   props[PROP_VIEW] =
     g_param_spec_object ("view",
                          "View",
@@ -270,6 +333,13 @@ adw_tab_item_class_init (AdwTabItemClass *klass)
                       0, G_MAXINT, 0,
                       G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
+  props[PROP_DISPLAY_HEIGHT] =
+    g_param_spec_int ("display-height",
+                      "Display Height",
+                      "Display Height",
+                      0, G_MAXINT, 0,
+                      G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
   props[PROP_INVERTED] =
     g_param_spec_boolean ("inverted",
                           "Inverted",
@@ -408,6 +478,39 @@ adw_tab_item_set_display_width (AdwTabItem *self,
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DISPLAY_WIDTH]);
 }
 
+int
+adw_tab_item_get_display_height (AdwTabItem *self)
+{
+  AdwTabItemPrivate *priv;
+
+  g_return_val_if_fail (ADW_IS_TAB_ITEM (self), 0);
+
+  priv = adw_tab_item_get_instance_private (self);
+
+  return priv->display_height;
+}
+
+void
+adw_tab_item_set_display_height (AdwTabItem *self,
+                                 int         height)
+{
+  AdwTabItemPrivate *priv;
+
+  g_return_if_fail (ADW_IS_TAB_ITEM (self));
+  g_return_if_fail (height >= 0);
+
+  priv = adw_tab_item_get_instance_private (self);
+
+  if (priv->display_height == height)
+    return;
+
+  priv->display_height = height;
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DISPLAY_HEIGHT]);
+}
+
 gboolean
 adw_tab_item_get_hovering (AdwTabItem *self)
 {
diff --git a/src/adw-tab-list-base.c b/src/adw-tab-list-base.c
index 4edd8de9..4186e6bd 100644
--- a/src/adw-tab-list-base.c
+++ b/src/adw-tab-list-base.c
@@ -3089,17 +3089,7 @@ get_n_columns (AdwTabListBase *self,
   if (!WRAP || for_width < 0)
     return priv->n_tabs;
 
-  for (l = priv->tabs; l; l = l->next) {
-    TabInfo *info = l->data;
-    int child_width;
-
-    gtk_widget_measure (GTK_WIDGET (info->tab), GTK_ORIENTATION_HORIZONTAL, -1,
-                        NULL, &child_width, NULL, NULL);
-
-    tab_width = MAX (tab_width, child_width);
-  }
-
-  return (int) floor ((double) for_width / tab_width);
+  return CLAMP ((int) ceil ((double) for_width / 300.0), 1, 8);
 }
 
 static void
@@ -3158,8 +3148,12 @@ adw_tab_list_base_measure (GtkWidget      *widget,
       for (l = priv->tabs; l; l = l->next) {
         TabInfo *info = l->data;
         int child_height;
+        int child_width = -1;
 
-        gtk_widget_measure (GTK_WIDGET (info->tab), orientation, -1,
+        if (for_size >= 0)
+          child_width = for_size / n_columns;
+
+        gtk_widget_measure (GTK_WIDGET (info->tab), orientation, child_width,
                             NULL, &child_height, NULL, NULL);
 
         min = MAX (min, child_height);
@@ -3280,14 +3274,15 @@ adw_tab_list_base_size_allocate (GtkWidget *widget,
   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
 
   if (WRAP) {
+    int n_columns = get_n_columns (self, width);
+
     tab_height = 0;
 
     for (l = priv->tabs; l; l = l->next) {
       TabInfo *info = l->data;
       int child_width, child_height;
 
-      gtk_widget_measure (GTK_WIDGET (info->tab), GTK_ORIENTATION_HORIZONTAL, -1,
-                          NULL, &child_width, NULL, NULL);
+      child_width = width / n_columns;
       gtk_widget_measure (GTK_WIDGET (info->tab), GTK_ORIENTATION_VERTICAL, child_width,
                           NULL, &child_height, NULL, NULL);
 
diff --git a/src/adw-tab-overview.c b/src/adw-tab-overview.c
new file mode 100644
index 00000000..c0233d1c
--- /dev/null
+++ b/src/adw-tab-overview.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "adw-tab-overview.h"
+
+#include "adw-animation-private.h"
+#include "adw-tab-grid-private.h"
+#include "adw-widget-utils-private.h"
+
+#define TRANSITION_DURATION 400
+#define OFFSET_FACTOR 0.1f
+#define THUMBNAIL_BORDER_RADIUS 8
+
+/**
+ * AdwTabOverview:
+ *
+ * Since: 1.0
+ */
+
+struct _AdwTabOverview
+{
+  GtkWidget parent_instance;
+
+  GtkWidget *child;
+  GtkWidget *overview;
+  AdwTabView *view;
+
+  AdwTabListBase *grid;
+
+  gboolean is_open;
+  AdwAnimation *open_animation;
+  gdouble progress;
+
+  GtkWidget *hidden_thumbnail;
+  GdkPaintable *transition_paintable;
+
+  GskGLShader *shader;
+  gboolean shader_compiled;
+};
+
+static void adw_tab_overview_buildable_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (AdwTabOverview, adw_tab_overview, GTK_TYPE_WIDGET,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, adw_tab_overview_buildable_init))
+
+static GtkBuildableIface *parent_buildable_iface;
+
+enum {
+  PROP_0,
+  PROP_VIEW,
+  PROP_CHILD,
+  LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP];
+
+enum {
+  SIGNAL_EXTRA_DRAG_DROP,
+  SIGNAL_LAST_SIGNAL,
+};
+
+static guint signals[SIGNAL_LAST_SIGNAL];
+
+static gboolean
+extra_drag_drop_cb (AdwTabOverview *self,
+                    AdwTabPage     *page,
+                    GValue         *value)
+{
+  gboolean ret = GDK_EVENT_PROPAGATE;
+
+  g_signal_emit (self, signals[SIGNAL_EXTRA_DRAG_DROP], 0, page, value, &ret);
+
+  return ret;
+}
+
+static void
+view_destroy_cb (AdwTabOverview *self)
+{
+  adw_tab_overview_set_view (self, NULL);
+}
+
+static void
+notify_selected_page_cb (AdwTabOverview *self)
+{
+  AdwTabPage *page = adw_tab_view_get_selected_page (self->view);
+
+  if (!page)
+    return;
+
+  adw_tab_list_base_select_page (self->grid, page);
+}
+
+static void
+notify_pinned_cb (AdwTabPage     *page,
+                  GParamSpec     *pspec,
+                  AdwTabOverview *self)
+{
+}
+
+static void
+page_attached_cb (AdwTabOverview *self,
+                  AdwTabPage     *page,
+                  int             position)
+{
+  g_signal_connect_object (page, "notify::pinned",
+                           G_CALLBACK (notify_pinned_cb), self,
+                           0);
+}
+
+static void
+page_detached_cb (AdwTabOverview *self,
+                  AdwTabPage     *page,
+                  int             position)
+{
+  g_signal_handlers_disconnect_by_func (page, notify_pinned_cb, self);
+}
+
+static void
+open_animation_value_cb (double          value,
+                         AdwTabOverview *self)
+{
+  self->progress = value;
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+open_animation_done_cb (AdwTabOverview *self)
+{
+  g_clear_object (&self->open_animation);
+
+  gtk_widget_set_opacity (self->hidden_thumbnail, 1);
+  g_clear_object (&self->hidden_thumbnail);
+  g_clear_object (&self->transition_paintable);
+
+  if (!self->is_open) {
+//    grid.did_close ();
+    gtk_widget_hide (self->overview);
+    gtk_widget_set_can_target (self->overview, FALSE);
+  }
+}
+
+static void
+set_open (AdwTabOverview *self,
+          gboolean        is_open)
+{
+  AdwTabPage *selected_page;
+  AdwTabGrid *grid;
+  GtkPicture *picture;
+
+  self->is_open = is_open;
+
+  if (is_open || !self->open_animation) {
+      if (self->open_animation)
+        adw_animation_stop (self->open_animation);
+
+      gtk_widget_show (self->overview);
+      gtk_widget_set_can_target (self->overview, TRUE);
+  }
+
+//  if (is_open)
+//    grid.will_open ();
+
+  selected_page = adw_tab_view_get_selected_page (self->view);
+
+  picture = adw_tab_grid_get_transition_picture (ADW_TAB_GRID (self->grid));
+
+  self->hidden_thumbnail = g_object_ref (GTK_WIDGET (picture));
+  gtk_widget_set_opacity (self->hidden_thumbnail, 0);
+
+  self->transition_paintable = g_object_ref (gtk_picture_get_paintable (picture));
+
+  self->open_animation =
+    adw_animation_new (GTK_WIDGET (self),
+                       self->progress,
+                       is_open ? 1 : 0,
+                       TRANSITION_DURATION,
+                       (AdwAnimationTargetFunc) open_animation_value_cb,
+                       self);
+
+  g_signal_connect_swapped (self->open_animation, "done", G_CALLBACK (open_animation_done_cb), self);
+
+  adw_animation_start (self->open_animation);
+}
+
+static void
+ensure_shader (AdwTabOverview *self)
+{
+  GtkNative *native;
+  GskRenderer *renderer;
+  g_autoptr (GError) error = NULL;
+
+  if (self->shader)
+    return;
+
+  self->shader = gsk_gl_shader_new_from_resource ("/org/gnome/Adwaita/glsl/tab-overview.glsl");
+
+  native = gtk_widget_get_native (GTK_WIDGET (self));
+  renderer = gtk_native_get_renderer (native);
+
+  self->shader_compiled = gsk_gl_shader_compile (self->shader, renderer, &error);
+
+  if (error) {
+    /* If shaders aren't supported, the error doesn't matter and we just
+     * silently fall back */
+    if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+      g_critical ("Couldn't compile shader: %s\n", error->message);
+  }
+}
+
+static void
+adw_tab_overview_snapshot (GtkWidget   *widget,
+                           GtkSnapshot *snapshot)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (widget);
+
+  if (self->open_animation) {
+    graphene_rect_t view_bounds, thumbnail_bounds, transition_bounds;
+    graphene_point_t view_center;
+    int width, height;
+    float scale;
+    GskRoundedRect transition_rect;
+    GdkRGBA borders_color;
+    GtkSnapshot *child_snapshot;
+    g_autoptr (GskRenderNode) child_node = NULL;
+
+    ensure_shader (self);
+
+    width = gtk_widget_get_width (widget);
+    height = gtk_widget_get_height (widget);
+    scale = 1 - OFFSET_FACTOR * (float) (1 - CLAMP (self->progress, 0, 1));
+
+    if (!gtk_widget_compute_bounds (GTK_WIDGET (self->view), widget, &view_bounds))
+      g_critical ("View must be inside the overview"); // TODO
+
+    if (!gtk_widget_compute_bounds (self->hidden_thumbnail, widget, &thumbnail_bounds))
+      graphene_rect_init (&thumbnail_bounds, 0, 0, 0, 0);
+
+    graphene_rect_get_center (&view_bounds, &view_center);
+
+    graphene_rect_interpolate (&view_bounds, &thumbnail_bounds,
+                               self->progress, &transition_bounds);
+
+    gsk_rounded_rect_init_from_rect (&transition_rect,
+                                     &GRAPHENE_RECT_INIT (0, 0,
+                                                          transition_bounds.size.width,
+                                                          transition_bounds.size.height),
+                                     (float) (THUMBNAIL_BORDER_RADIUS * self->progress));
+
+    if (!gtk_style_context_lookup_color (gtk_widget_get_style_context (widget),
+                                         "borders", &borders_color))
+      borders_color.alpha = 0;
+
+    /* Draw overview */
+    gtk_snapshot_save (snapshot);
+    gtk_snapshot_push_opacity (snapshot, self->progress);
+    gtk_snapshot_translate (snapshot, &view_center);
+    gtk_snapshot_scale (snapshot, scale, scale);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-view_center.x, -view_center.y));
+    gtk_widget_snapshot_child (widget, self->overview, snapshot);
+    gtk_snapshot_pop (snapshot);
+    gtk_snapshot_restore (snapshot);
+
+    /* Draw the transition thumbnail. Unfortunately, since GTK widgets have
+     * integer sizes, we can't use a real widget for this and have to custom
+     * draw it instead. We also want to interpolate border-radius. */
+    if (self->transition_paintable) {
+      gtk_snapshot_save (snapshot);
+      gtk_snapshot_translate (snapshot, &transition_bounds.origin);
+      gtk_snapshot_append_outset_shadow (snapshot, &transition_rect,
+                                         &borders_color, 0, 0, 1, 0);
+      gtk_snapshot_push_rounded_clip (snapshot, &transition_rect);
+      gdk_paintable_snapshot (self->transition_paintable,
+                              GDK_SNAPSHOT (snapshot),
+                              transition_rect.bounds.size.width,
+                              transition_rect.bounds.size.height);
+      gtk_snapshot_pop (snapshot);
+      gtk_snapshot_restore (snapshot);
+    }
+
+    /* Draw the child */
+    scale += OFFSET_FACTOR;
+    gtk_snapshot_save (snapshot);
+    gtk_snapshot_translate (snapshot, &view_center);
+    gtk_snapshot_scale (snapshot, scale, scale);
+    gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-view_center.x, -view_center.y));
+
+    child_snapshot = gtk_snapshot_new ();
+
+    if (self->shader_compiled) {
+      graphene_vec2_t origin, size;
+      double opacity;
+
+      opacity = 1 - CLAMP (self->progress, 0, 1);
+      graphene_point_to_vec2 (&view_bounds.origin, &origin);
+      graphene_vec2_init (&size, view_bounds.size.width, view_bounds.size.height);
+
+      gtk_snapshot_push_gl_shader (child_snapshot,
+                                   self->shader,
+                                   &GRAPHENE_RECT_INIT (0, 0, width, height),
+                                   gsk_gl_shader_format_args (self->shader,
+                                                              "opacity", opacity,
+                                                              "origin", &origin,
+                                                              "size", &size,
+                                                              NULL));
+    } else {
+      gtk_snapshot_push_opacity (child_snapshot, 1 - self->progress);
+    }
+
+    gtk_widget_snapshot_child (widget, self->child, child_snapshot);
+
+    if (self->shader_compiled)
+      gtk_snapshot_gl_shader_pop_texture (child_snapshot);
+
+    gtk_snapshot_pop (child_snapshot);
+
+    child_node = gtk_snapshot_free_to_node (child_snapshot);
+    gtk_snapshot_append_node (snapshot, child_node);
+
+    gtk_snapshot_restore (snapshot);
+
+    return;
+  }
+
+  if (self->progress > 0.5)
+    gtk_widget_snapshot_child (widget, self->overview, snapshot);
+
+  if (!self->child)
+    return;
+
+  /* We don't want to actually draw the child, but we do need it
+   * to redraw so that it can be displayed by the paintables */
+  if (self->progress > 0.5) {
+    g_autoptr (GtkSnapshot) child_snapshot = gtk_snapshot_new ();
+
+    gtk_widget_snapshot_child (widget, self->child, child_snapshot);
+  } else {
+    gtk_widget_snapshot_child (widget, self->child, snapshot);
+  }
+}
+
+static void
+adw_tab_overview_unrealize (GtkWidget *widget)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (widget);
+
+  GTK_WIDGET_CLASS (adw_tab_overview_parent_class)->unrealize (widget);
+
+  g_clear_object (&self->shader);
+}
+
+static void
+adw_tab_overview_dispose (GObject *object)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (object);
+
+  adw_tab_overview_set_view (self, NULL);
+  adw_tab_overview_set_child (self, NULL);
+
+  gtk_widget_unparent (GTK_WIDGET (self->overview));
+
+  G_OBJECT_CLASS (adw_tab_overview_parent_class)->dispose (object);
+}
+
+static void
+adw_tab_overview_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (object);
+
+  switch (prop_id) {
+  case PROP_VIEW:
+    g_value_set_object (value, adw_tab_overview_get_view (self));
+    break;
+  case PROP_CHILD:
+    g_value_set_object (value, adw_tab_overview_get_child (self));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_tab_overview_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (object);
+
+  switch (prop_id) {
+  case PROP_VIEW:
+    adw_tab_overview_set_view (self, g_value_get_object (value));
+    break;
+  case PROP_CHILD:
+    adw_tab_overview_set_child (self, g_value_get_object (value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+overview_open_cb (GtkWidget  *widget,
+                  const char *action_name,
+                  GVariant   *param)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (widget);
+
+  adw_tab_overview_open (self);
+}
+
+static void
+adw_tab_overview_class_init (AdwTabOverviewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = adw_tab_overview_dispose;
+  object_class->get_property = adw_tab_overview_get_property;
+  object_class->set_property = adw_tab_overview_set_property;
+
+  widget_class->snapshot = adw_tab_overview_snapshot;
+  widget_class->unrealize = adw_tab_overview_unrealize;
+  widget_class->compute_expand = adw_widget_compute_expand;
+
+  /**
+   * AdwTabOverview:view: (attributes org.gtk.Property.get=adw_tab_overview_get_view 
org.gtk.Property.set=adw_tab_overview_set_view)
+   *
+   * The tab view the overview controls.
+   *
+   * Since: 1.0
+   */
+  props[PROP_VIEW] =
+    g_param_spec_object ("view",
+                         _("View"),
+                         _("The tab view the overview controls"),
+                         ADW_TYPE_TAB_VIEW,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * AdwTabOverview:child: (attributes org.gtk.Property.get=adw_tab_overview_get_child 
org.gtk.Property.set=adw_tab_overview_set_child)
+   *
+   * The child widget.
+   *
+   * Since: 1.0
+   */
+  props[PROP_CHILD] =
+      g_param_spec_object ("child",
+                           "Child",
+                           "The child widget",
+                           GTK_TYPE_WIDGET,
+                           G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+
+  /**
+   * AdwTabOverview::extra-drag-drop:
+   * @self: a `AdwTabOverview`
+   * @page: the page matching the tab the content was dropped onto
+   * @value: the `GValue` being dropped
+   *
+   * This signal is emitted when content is dropped onto a tab.
+   *
+   * The content must be of one of the types set up via
+   * [method@Adw.TabOverview.setup_extra_drop_target].
+   *
+   * See [signal@Gtk.DropTarget::drop].
+   *
+   * Returns: whether the drop was accepted for @page
+   *
+   * Since: 1.0
+   */
+  signals[SIGNAL_EXTRA_DRAG_DROP] =
+    g_signal_new ("extra-drag-drop",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  g_signal_accumulator_first_wins, NULL, NULL,
+                  G_TYPE_BOOLEAN,
+                  2,
+                  ADW_TYPE_TAB_PAGE,
+                  G_TYPE_VALUE);
+
+  gtk_widget_class_install_action (widget_class, "overview.open", NULL, overview_open_cb);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/Adwaita/ui/adw-tab-overview.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, AdwTabOverview, overview);
+  gtk_widget_class_bind_template_child (widget_class, AdwTabOverview, grid);
+  gtk_widget_class_bind_template_callback (widget_class, extra_drag_drop_cb);
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+  gtk_widget_class_set_css_name (widget_class, "taboverview");
+}
+
+static void
+adw_tab_overview_init (AdwTabOverview *self)
+{
+  g_type_ensure (ADW_TYPE_TAB_GRID);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  adw_tab_grid_set_tab_overview (ADW_TAB_GRID (self->grid), self);
+}
+
+static void
+adw_tab_overview_buildable_add_child (GtkBuildable *buildable,
+                                      GtkBuilder   *builder,
+                                      GObject      *child,
+                                      const char   *type)
+{
+  AdwTabOverview *self = ADW_TAB_OVERVIEW (buildable);
+
+  if (!self->overview)
+    parent_buildable_iface->add_child (buildable, builder, child, type);
+  else if (GTK_IS_WIDGET (child))
+    adw_tab_overview_set_child (self, GTK_WIDGET (child));
+  else
+    parent_buildable_iface->add_child (buildable, builder, child, type);
+}
+
+static void
+adw_tab_overview_buildable_init (GtkBuildableIface *iface)
+{
+  parent_buildable_iface = g_type_interface_peek_parent (iface);
+
+  iface->add_child = adw_tab_overview_buildable_add_child;
+}
+
+/**
+ * adw_tab_overview_new:
+ *
+ * Creates a new `AdwTabOverview`.
+ *
+ * Returns: the newly created `AdwTabOverview`
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+adw_tab_overview_new (void)
+{
+  return g_object_new (ADW_TYPE_TAB_OVERVIEW, NULL);
+}
+
+/**
+ * adw_tab_overview_get_view: (attributes org.gtk.Method.get_property=view)
+ * @self: a `AdwTabOverview`
+ *
+ * Gets the tab view @self controls.
+ *
+ * Returns: (transfer none) (nullable): the tab view
+ *
+ * Since: 1.0
+ */
+AdwTabView *
+adw_tab_overview_get_view (AdwTabOverview *self)
+{
+  g_return_val_if_fail (ADW_IS_TAB_OVERVIEW (self), NULL);
+
+  return self->view;
+}
+
+/**
+ * adw_tab_overview_set_view: (attributes org.gtk.Method.set_property=view)
+ * @self: a `AdwTabOverview`
+ * @view: (nullable): a tab view
+ *
+ * Sets the tab view to control.
+ *
+ * Since: 1.0
+ */
+void
+adw_tab_overview_set_view (AdwTabOverview *self,
+                           AdwTabView     *view)
+{
+  g_return_if_fail (ADW_IS_TAB_OVERVIEW (self));
+  g_return_if_fail (view == NULL || ADW_IS_TAB_VIEW (view));
+
+  if (self->view == view)
+    return;
+
+  if (self->view) {
+    int i, n;
+
+    g_signal_handlers_disconnect_by_func (self->view, notify_selected_page_cb, self);
+    g_signal_handlers_disconnect_by_func (self->view, page_attached_cb, self);
+    g_signal_handlers_disconnect_by_func (self->view, page_detached_cb, self);
+    g_signal_handlers_disconnect_by_func (self->view, view_destroy_cb, self);
+
+    n = adw_tab_view_get_n_pages (self->view);
+
+    for (i = 0; i < n; i++)
+      page_detached_cb (self, adw_tab_view_get_nth_page (self->view, i), i);
+
+    adw_tab_list_base_set_view (self->grid, NULL);
+  }
+
+  g_set_object (&self->view, view);
+
+  if (self->view) {
+    int i, n;
+
+    adw_tab_list_base_set_view (self->grid, view);
+
+    g_signal_connect_object (self->view, "notify::selected-page",
+                             G_CALLBACK (notify_selected_page_cb), self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (self->view, "page-attached",
+                             G_CALLBACK (page_attached_cb), self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (self->view, "page-detached",
+                             G_CALLBACK (page_detached_cb), self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (self->view, "destroy",
+                             G_CALLBACK (view_destroy_cb), self,
+                             G_CONNECT_SWAPPED);
+
+    n = adw_tab_view_get_n_pages (self->view);
+
+    for (i = 0; i < n; i++)
+      page_attached_cb (self, adw_tab_view_get_nth_page (self->view, i), i);
+  }
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VIEW]);
+}
+
+/**
+ * adw_tab_overview_get_child: (attributes org.gtk.Method.get_property=child)
+ * @self: a `AdwTabOveview`
+ *
+ * Gets the child widget of @self.
+ *
+ * Returns: (nullable) (transfer none): the child widget of @self
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+adw_tab_overview_get_child (AdwTabOverview *self)
+{
+  g_return_val_if_fail (ADW_IS_TAB_OVERVIEW (self), NULL);
+
+  return self->child;
+}
+
+/**
+ * adw_tab_overview_set_child: (attributes org.gtk.Method.set_property=child)
+ * @self: a `AdwTabOverview`
+ * @child: (nullable): the child widget
+ *
+ * Sets the child widget of @self.
+ *
+ * Since: 1.0
+ */
+void
+adw_tab_overview_set_child (AdwTabOverview *self,
+                            GtkWidget      *child)
+{
+  g_return_if_fail (ADW_IS_TAB_OVERVIEW (self));
+  g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
+
+  if (self->child == child)
+    return;
+
+  if (self->child)
+    gtk_widget_unparent (self->child);
+
+  self->child = child;
+
+  if (self->child)
+    gtk_widget_insert_after (self->child, GTK_WIDGET (self), NULL);
+
+  g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CHILD]);
+}
+
+/**
+ * adw_tab_overview_open:
+ * @self: a `AdwTabOverview`
+ *
+ * Opens the overview.
+ *
+ * Since: 1.0
+ */
+void
+adw_tab_overview_open (AdwTabOverview *self)
+{
+  g_return_if_fail (ADW_IS_TAB_OVERVIEW (self));
+
+  if (self->is_open)
+    return;
+
+  set_open (self, TRUE);
+}
+
+// TODO
+void
+adw_tab_overview_close (AdwTabOverview *self)
+{
+  g_return_if_fail (ADW_IS_TAB_OVERVIEW (self));
+
+  if (!self->is_open)
+    return;
+
+  set_open (self, FALSE);
+}
+
+/**
+ * adw_tab_overview_setup_extra_drop_target:
+ * @self: a `AdwTabOverview`
+ * @actions: the supported actions
+ * @types: (nullable) (transfer none) (array length=n_types):
+ *   all supported `GType`s that can be dropped
+ * @n_types: number of @types
+ *
+ * Sets the supported types for this drop target.
+ *
+ * Sets up an extra drop target on tabs.
+ *
+ * This allows to drag arbitrary content onto tabs, for example URLs in a web
+ * browser.
+ *
+ * If a tab is hovered for a certain period of time while dragging the content,
+ * it will be automatically selected.
+ *
+ * The [signal@Adw.TabOverview::extra-drag-drop] signal can be used to handle the
+ * drop.
+ *
+ * Since: 1.0
+ */
+void
+adw_tab_overview_setup_extra_drop_target (AdwTabOverview *self,
+                                          GdkDragAction   actions,
+                                          GType          *types,
+                                          gsize           n_types)
+{
+  g_return_if_fail (ADW_IS_TAB_OVERVIEW (self));
+  g_return_if_fail (n_types == 0 || types != NULL);
+
+  adw_tab_list_base_setup_extra_drop_target (self->grid, actions, types, n_types);
+}
diff --git a/src/adw-tab-overview.h b/src/adw-tab-overview.h
new file mode 100644
index 00000000..0bdd08af
--- /dev/null
+++ b/src/adw-tab-overview.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-version.h"
+
+#include <gtk/gtk.h>
+#include "adw-tab-view.h"
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_TAB_OVERVIEW (adw_tab_overview_get_type())
+
+ADW_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (AdwTabOverview, adw_tab_overview, ADW, TAB_OVERVIEW, GtkWidget)
+
+ADW_AVAILABLE_IN_ALL
+GtkWidget *adw_tab_overview_new (void);
+
+ADW_AVAILABLE_IN_ALL
+AdwTabView *adw_tab_overview_get_view (AdwTabOverview *self);
+ADW_AVAILABLE_IN_ALL
+void        adw_tab_overview_set_view (AdwTabOverview *self,
+                                       AdwTabView     *view);
+
+ADW_AVAILABLE_IN_ALL
+GtkWidget *adw_tab_overview_get_child (AdwTabOverview *self);
+ADW_AVAILABLE_IN_ALL
+void       adw_tab_overview_set_child (AdwTabOverview *self,
+                                       GtkWidget      *child);
+
+ADW_AVAILABLE_IN_ALL
+void adw_tab_overview_open  (AdwTabOverview *self);
+ADW_AVAILABLE_IN_ALL
+void adw_tab_overview_close (AdwTabOverview *self);
+
+ADW_AVAILABLE_IN_ALL
+void adw_tab_overview_setup_extra_drop_target (AdwTabOverview *self,
+                                               GdkDragAction   actions,
+                                               GType          *types,
+                                               gsize           n_types);
+
+G_END_DECLS
diff --git a/src/adw-tab-overview.ui b/src/adw-tab-overview.ui
new file mode 100644
index 00000000..ea2e6b5d
--- /dev/null
+++ b/src/adw-tab-overview.ui
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="AdwTabOverview" parent="GtkWidget">
+    <child>
+      <object class="GtkOverlay" id="overview">
+        <property name="visible">False</property>
+        <property name="can-target">False</property>
+        <child>
+          <object class="GtkScrolledWindow" id="swindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="child">
+              <object class="AdwTabGrid" id="grid">
+                <signal name="extra-drag-drop" handler="extra_drag_drop_cb" swapped="true"/>
+              </object>
+            </property>
+<!--                <property name="child">
+                  <object class="AdwClamp">
+                    <property name="margin-top">50</property>
+                    <property name="margin-bottom">24</property>
+                    <property name="margin-start">24</property>
+                    <property name="margin-end">24</property>
+                    <property name="maximum-size">4000</property
+                      <object class="GtkFlowBox" id="flowbox">
+                        <property name="halign">fill</property>
+                        <property name="valign">center</property>
+                        <property name="column-spacing">24</property>
+                        <property name="row-spacing">24</property>
+                        <property name="selection-mode">none</property>
+                        <property name="activate-on-single-click">True</property>
+                        <property name="homogeneous">True</property>
+                        <property name="min-children-per-line">2</property>
+                      </object>
+                    </property>
+                  </object>-->
+          </object>
+        </child>
+        <child type="overlay">
+          <object class="GtkHeaderBar" id="headerbar">
+            <property name="valign">start</property>
+            <property name="title-widget">
+              <object class="AdwWindowTitle"/>
+            </property>
+            <layout>
+              <property name="measure">True</property>
+            </layout>
+            <style>
+              <class name="flat"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/adw-tab-thumbnail-private.h b/src/adw-tab-thumbnail-private.h
new file mode 100644
index 00000000..a2aae6fb
--- /dev/null
+++ b/src/adw-tab-thumbnail-private.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-tab-item-private.h"
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_TAB_THUMBNAIL (adw_tab_thumbnail_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwTabThumbnail, adw_tab_thumbnail, ADW, TAB_THUMBNAIL, AdwTabItem)
+
+GtkPicture *adw_tab_thumbnail_get_picture (AdwTabThumbnail *self);
+
+G_END_DECLS
diff --git a/src/adw-tab-thumbnail.c b/src/adw-tab-thumbnail.c
new file mode 100644
index 00000000..f6262a20
--- /dev/null
+++ b/src/adw-tab-thumbnail.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include "config.h"
+#include "adw-tab-thumbnail-private.h"
+
+#define MIN_ASPECT_RATIO 0.8
+#define MAX_ASPECT_RATIO 1.6
+#define XALIGN 0.5
+#define YALIGN 0.5
+
+#define ADW_TYPE_TAB_PAINTABLE (adw_tab_paintable_get_type ())
+
+G_DECLARE_FINAL_TYPE (AdwTabPaintable, adw_tab_paintable, ADW, TAB_PAINTABLE, GObject)
+
+struct _AdwTabPaintable
+{
+  GObject parent_instance;
+
+  GtkWidget *view;
+  GtkWidget *child;
+
+  GdkPaintable *paintable;
+  GdkPaintable *view_paintable;
+  GdkPaintable *cached_paintable;
+
+  GdkRGBA cached_bg;
+  int cached_width;
+  int cached_height;
+  bool schedule_clear_cache;
+};
+
+static double
+adw_tab_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
+{
+  AdwTabPaintable *self = ADW_TAB_PAINTABLE (paintable);
+  int width = gtk_widget_get_width (self->view);
+  int height = gtk_widget_get_height (self->view);
+  double ratio = (double) width / height;
+
+  return CLAMP (ratio, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO);
+}
+
+static GdkPaintable *
+adw_tab_paintable_get_current_image (GdkPaintable *paintable)
+{
+  AdwTabPaintable *self = ADW_TAB_PAINTABLE (paintable);
+
+  if (self->cached_paintable)
+    return g_object_ref (self->cached_paintable);
+
+  return gdk_paintable_get_current_image (self->paintable);
+}
+
+static void
+snapshot_paintable (GdkSnapshot  *snapshot,
+                    double        width,
+                    double        height,
+                    GdkPaintable *paintable)
+{
+  double snapshot_ratio = width / height;
+  double paintable_ratio = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
+
+  if (paintable_ratio > snapshot_ratio) {
+    double new_width = width * paintable_ratio / snapshot_ratio;
+
+    gtk_snapshot_translate (GTK_SNAPSHOT (snapshot),
+                            &GRAPHENE_POINT_INIT ((float) (width - new_width) * XALIGN, 0));
+
+    width = new_width;
+  } if (paintable_ratio < snapshot_ratio) {
+    double new_height = height * snapshot_ratio / paintable_ratio;
+
+    gtk_snapshot_translate (GTK_SNAPSHOT (snapshot),
+                            &GRAPHENE_POINT_INIT (0, (float) (height - new_height) * YALIGN));
+
+    height = new_height;
+  }
+
+  gdk_paintable_snapshot (paintable, snapshot, width, height);
+}
+
+static void
+get_background_color (AdwTabPaintable *self,
+                      GdkRGBA         *rgba)
+{
+  GtkStyleContext *context = gtk_widget_get_style_context (self->view);
+
+  if (gtk_style_context_lookup_color (context, "theme_bg_color", rgba))
+    return;
+
+  rgba->red = 1;
+  rgba->green = 1;
+  rgba->blue = 1;
+  rgba->alpha = 1;
+}
+
+static void
+adw_tab_paintable_snapshot (GdkPaintable *paintable,
+                            GdkSnapshot  *snapshot,
+                            double        width,
+                            double        height)
+{
+  AdwTabPaintable *self = ADW_TAB_PAINTABLE (paintable);
+  GdkRGBA bg;
+
+  if (self->cached_paintable) {
+    gtk_snapshot_append_color (GTK_SNAPSHOT (snapshot), &self->cached_bg,
+                               &GRAPHENE_RECT_INIT (0, 0, width, height));
+
+    snapshot_paintable (snapshot, width, height, self->cached_paintable);
+
+    return;
+  }
+
+  if (!gtk_widget_get_mapped (self->child))
+    return;
+
+  get_background_color (self, &bg);
+  gtk_snapshot_append_color (GTK_SNAPSHOT (snapshot), &bg,
+                             &GRAPHENE_RECT_INIT (0, 0, width, height));
+
+  snapshot_paintable (snapshot, width, height, self->paintable);
+}
+
+static void
+adw_tab_paintable_iface_init (GdkPaintableInterface *iface)
+{
+  iface->get_intrinsic_aspect_ratio = adw_tab_paintable_get_intrinsic_aspect_ratio;
+  iface->get_current_image = adw_tab_paintable_get_current_image;
+  iface->snapshot = adw_tab_paintable_snapshot;
+}
+
+G_DEFINE_TYPE_WITH_CODE (AdwTabPaintable, adw_tab_paintable, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, adw_tab_paintable_iface_init))
+
+static void
+adw_tab_paintable_dispose (GObject *object)
+{
+  AdwTabPaintable *self = ADW_TAB_PAINTABLE (object);
+
+  g_clear_object (&self->paintable);
+  g_clear_object (&self->view_paintable);
+  g_clear_object (&self->cached_paintable);
+
+  G_OBJECT_CLASS (adw_tab_paintable_parent_class)->dispose (object);
+}
+
+static void
+adw_tab_paintable_class_init (AdwTabPaintableClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = adw_tab_paintable_dispose;
+}
+
+static void
+adw_tab_paintable_init (AdwTabPaintable *self)
+{
+}
+
+static void
+invalidate_contents_cb (AdwTabPaintable *self)
+{
+  gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+
+  if (self->schedule_clear_cache) {
+    g_clear_object (&self->cached_paintable);
+    self->schedule_clear_cache = FALSE;
+  }
+}
+
+static void
+child_map_cb (AdwTabPaintable *self)
+{
+  if (self->cached_paintable)
+    self->schedule_clear_cache = TRUE;
+}
+
+static void
+child_unmap_cb (AdwTabPaintable *self)
+{
+  self->cached_paintable = gdk_paintable_get_current_image (self->paintable);
+  self->cached_width = gtk_widget_get_width (self->view);
+  self->cached_height = gtk_widget_get_height (self->view);
+  get_background_color (self, &self->cached_bg);
+}
+
+static GdkPaintable *
+adw_tab_paintable_new (AdwTabView *view,
+                       AdwTabPage *page)
+{
+  AdwTabPaintable *self = g_object_new (ADW_TYPE_TAB_PAINTABLE, NULL);
+
+  self->view = GTK_WIDGET (view);
+  self->child = adw_tab_page_get_child (page);
+
+  self->paintable = gtk_widget_paintable_new (self->child);
+  self->view_paintable = gtk_widget_paintable_new (self->view);
+
+  g_signal_connect_swapped (self->paintable, "invalidate-contents", G_CALLBACK (invalidate_contents_cb), 
self);
+  g_signal_connect_swapped (self->view_paintable, "invalidate-size", G_CALLBACK 
(gdk_paintable_invalidate_size), self);
+
+  g_signal_connect_swapped (self->child, "map", G_CALLBACK (child_map_cb), self);
+  g_signal_connect_swapped (self->child, "unmap", G_CALLBACK (child_unmap_cb), self);
+
+  return GDK_PAINTABLE (self);
+}
+
+
+
+
+
+
+
+
+
+
+
+struct _AdwTabThumbnail
+{
+  AdwTabItem parent_instance;
+
+  GtkPicture *picture;
+  GtkWidget *contents;
+};
+
+G_DEFINE_TYPE (AdwTabThumbnail, adw_tab_thumbnail, ADW_TYPE_TAB_ITEM)
+
+static void
+update_tooltip (AdwTabThumbnail *self)
+{
+  AdwTabPage *page = adw_tab_item_get_page (ADW_TAB_ITEM (self));
+  const char *tooltip = adw_tab_page_get_tooltip (page);
+
+  if (tooltip && g_strcmp0 (tooltip, "") != 0)
+    gtk_widget_set_tooltip_markup (GTK_WIDGET (self), tooltip);
+  else
+    gtk_widget_set_tooltip_text (GTK_WIDGET (self),
+                                 adw_tab_page_get_title (page));
+}
+
+static void
+adw_tab_thumbnail_connect_page (AdwTabItem *item)
+{
+  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (item);
+  AdwTabView *view = adw_tab_item_get_view (item);
+  AdwTabPage *page = adw_tab_item_get_page (item);
+
+  gtk_picture_set_paintable (GTK_PICTURE (self->picture),
+                             adw_tab_paintable_new (view, page));
+
+  update_tooltip (self);
+
+  g_signal_connect_object (page, "notify::title",
+                           G_CALLBACK (update_tooltip), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::tooltip",
+                           G_CALLBACK (update_tooltip), self,
+                           G_CONNECT_SWAPPED);
+
+/*
+  update_selected (self);
+  update_state (self);
+  update_title (self);
+  update_tooltip (self);
+  update_spinner (self);
+  update_icons (self);
+  update_indicator (self);
+  update_needs_attention (self);
+  update_loading (self);
+
+  g_signal_connect_object (page, "notify::selected",
+                           G_CALLBACK (update_selected), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::title",
+                           G_CALLBACK (update_title), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::tooltip",
+                           G_CALLBACK (update_tooltip), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::icon",
+                           G_CALLBACK (update_icons), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::indicator-icon",
+                           G_CALLBACK (update_icons), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::indicator-activatable",
+                           G_CALLBACK (update_indicator), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::needs-attention",
+                           G_CALLBACK (update_needs_attention), self,
+                           G_CONNECT_SWAPPED);
+  g_signal_connect_object (page, "notify::loading",
+                           G_CALLBACK (update_loading), self,
+                           G_CONNECT_SWAPPED);
+*/
+}
+
+static void
+adw_tab_thumbnail_disconnect_page (AdwTabItem *item)
+{
+  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (item);
+  AdwTabPage *page = adw_tab_item_get_page (item);
+
+  gtk_picture_set_paintable (GTK_PICTURE (self->picture), NULL);
+
+  g_signal_handlers_disconnect_by_func (page, update_tooltip, self);
+/*
+
+  g_signal_handlers_disconnect_by_func (page, update_selected, self);
+  g_signal_handlers_disconnect_by_func (page, update_title, self);
+  g_signal_handlers_disconnect_by_func (page, update_tooltip, self);
+  g_signal_handlers_disconnect_by_func (page, update_icons, self);
+  g_signal_handlers_disconnect_by_func (page, update_indicator, self);
+  g_signal_handlers_disconnect_by_func (page, update_needs_attention, self);
+  g_signal_handlers_disconnect_by_func (page, update_loading, self);
+*/
+}
+
+static int
+adw_tab_thumbnail_measure_contents (AdwTabItem     *item,
+                                    GtkOrientation  orientation,
+                                    int             for_size)
+{
+  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (item);
+  int nat;
+
+  gtk_widget_measure (self->contents, orientation, for_size,
+                      NULL, &nat, NULL, NULL);
+
+  return nat;
+}
+
+static void
+adw_tab_thumbnail_allocate_contents (AdwTabItem    *item,
+                                     GtkAllocation *alloc,
+                                     int            baseline)
+{
+  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (item);
+  gtk_widget_size_allocate (self->contents, alloc, baseline);
+}
+
+static GtkSizeRequestMode
+adw_tab_thumbnail_get_request_mode (GtkWidget *widget)
+{
+  return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+static void
+adw_tab_thumbnail_constructed (GObject *object)
+{
+//  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (object);
+
+  G_OBJECT_CLASS (adw_tab_thumbnail_parent_class)->constructed (object);
+
+  /*
+  g_signal_connect_object (view, "notify::default-icon",
+                           G_CALLBACK (update_icons), object,
+                           G_CONNECT_SWAPPED);
+*/
+}
+
+static void
+adw_tab_thumbnail_dispose (GObject *object)
+{
+  AdwTabThumbnail *self = ADW_TAB_THUMBNAIL (object);
+
+  if (self->contents)
+    gtk_widget_unparent (self->contents);
+
+  G_OBJECT_CLASS (adw_tab_thumbnail_parent_class)->dispose (object);
+}
+
+static void
+adw_tab_thumbnail_class_init (AdwTabThumbnailClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  AdwTabItemClass *item_class = ADW_TAB_ITEM_CLASS (klass);
+
+  object_class->dispose = adw_tab_thumbnail_dispose;
+  object_class->constructed = adw_tab_thumbnail_constructed;
+
+  widget_class->get_request_mode = adw_tab_thumbnail_get_request_mode;
+
+  item_class->connect_page = adw_tab_thumbnail_connect_page;
+  item_class->disconnect_page = adw_tab_thumbnail_disconnect_page;
+  item_class->measure_contents = adw_tab_thumbnail_measure_contents;
+  item_class->allocate_contents = adw_tab_thumbnail_allocate_contents;
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/Adwaita/ui/adw-tab-thumbnail.ui");
+  gtk_widget_class_bind_template_child (widget_class, AdwTabThumbnail, picture);
+  gtk_widget_class_bind_template_child (widget_class, AdwTabThumbnail, contents);
+
+  gtk_widget_class_set_css_name (widget_class, "tabthumbnail");
+}
+
+static void
+adw_tab_thumbnail_init (AdwTabThumbnail *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
+}
+
+GtkPicture *
+adw_tab_thumbnail_get_picture (AdwTabThumbnail *self)
+{
+  g_return_val_if_fail (ADW_IS_TAB_THUMBNAIL (self), NULL);
+
+  return GTK_PICTURE (self->picture);
+}
diff --git a/src/adw-tab-thumbnail.ui b/src/adw-tab-thumbnail.ui
new file mode 100644
index 00000000..a0a40a63
--- /dev/null
+++ b/src/adw-tab-thumbnail.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="libadwaita">
+  <requires lib="gtk" version="4.0"/>
+  <template class="AdwTabThumbnail" parent="AdwTabItem">
+    <child>
+      <object class="GtkBox" id="contents">
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <property name="vexpand">False</property>
+        <child>
+          <object class="GtkPicture" id="picture">
+            <property name="can-shrink">True</property>
+            <property name="keep-aspect-ratio">False</property>
+            <property name="overflow">hidden</property>
+            <property name="vexpand">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="ellipsize">end</property>
+            <binding name="label">
+              <lookup name="title" type="AdwTabPage">
+                <lookup name="page">AdwTabThumbnail</lookup>
+              </lookup>
+            </binding>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/adw-tab.c b/src/adw-tab.c
index 8759af96..fd5c13fd 100644
--- a/src/adw-tab.c
+++ b/src/adw-tab.c
@@ -345,53 +345,38 @@ adw_tab_disconnect_page (AdwTabItem *item)
   g_signal_handlers_disconnect_by_func (page, update_loading, self);
 }
 
-static void
-adw_tab_measure (GtkWidget      *widget,
-                 GtkOrientation  orientation,
-                 int             for_size,
-                 int            *minimum,
-                 int            *natural,
-                 int            *minimum_baseline,
-                 int            *natural_baseline)
+static int
+adw_tab_measure_contents (AdwTabItem     *item,
+                          GtkOrientation  orientation,
+                          int             for_size)
 {
-  AdwTab *self = ADW_TAB (widget);
-  gboolean pinned = adw_tab_item_get_pinned (ADW_TAB_ITEM (self));
-  int min = 0, nat = 0;
+  AdwTab *self = ADW_TAB (item);
 
   if (orientation == GTK_ORIENTATION_HORIZONTAL) {
-    nat = pinned ? BASE_WIDTH_PINNED : BASE_WIDTH;
+    gboolean pinned = adw_tab_item_get_pinned (ADW_TAB_ITEM (self));
+
+    return pinned ? BASE_WIDTH_PINNED : BASE_WIDTH;
   } else {
-    int child_min, child_nat;
+    int nat = 0, child_nat;
 
     gtk_widget_measure (self->icon_stack, orientation, for_size,
-                        &child_min, &child_nat, NULL, NULL);
-    min = MAX (min, child_min);
+                        NULL, &child_nat, NULL, NULL);
     nat = MAX (nat, child_nat);
 
     gtk_widget_measure (self->title, orientation, for_size,
-                        &child_min, &child_nat, NULL, NULL);
-    min = MAX (min, child_min);
+                        NULL, &child_nat, NULL, NULL);
     nat = MAX (nat, child_nat);
 
     gtk_widget_measure (self->close_btn, orientation, for_size,
-                        &child_min, &child_nat, NULL, NULL);
-    min = MAX (min, child_min);
+                        NULL, &child_nat, NULL, NULL);
     nat = MAX (nat, child_nat);
 
     gtk_widget_measure (self->indicator_btn, orientation, for_size,
-                        &child_min, &child_nat, NULL, NULL);
-    min = MAX (min, child_min);
+                        NULL, &child_nat, NULL, NULL);
     nat = MAX (nat, child_nat);
-  }
 
-  if (minimum)
-    *minimum = min;
-  if (natural)
-    *natural = nat;
-  if (minimum_baseline)
-    *minimum_baseline = -1;
-  if (natural_baseline)
-    *natural_baseline = -1;
+    return nat;
+  }
 }
 
 static inline void
@@ -425,10 +410,11 @@ allocate_child (GtkWidget     *child,
 }
 
 static void
-allocate_contents (AdwTab        *self,
-                   GtkAllocation *alloc,
-                   int            baseline)
+adw_tab_allocate_contents (AdwTabItem    *item,
+                           GtkAllocation *alloc,
+                           int            baseline)
 {
+  AdwTab *self = ADW_TAB (item);
   int indicator_width, close_width, icon_width, title_width;
   int center_x, center_width = 0;
   int start_width = 0, end_width = 0;
@@ -495,34 +481,6 @@ allocate_contents (AdwTab        *self,
     allocate_child (self->title, alloc, center_x, center_width, baseline);
 }
 
-static void
-adw_tab_size_allocate (GtkWidget *widget,
-                       int        width,
-                       int        height,
-                       int        baseline)
-{
-  AdwTab *self = ADW_TAB (widget);
-  int display_width = adw_tab_item_get_display_width (ADW_TAB_ITEM (self));
-  GtkAllocation child_alloc;
-  int allocated_width, width_diff;
-
-  if (!self->icon_stack ||
-      !self->indicator_btn ||
-      !self->title ||
-      !self->close_btn)
-    return;
-
-  allocated_width = gtk_widget_get_allocated_width (widget);
-  width_diff = MAX (0, display_width - allocated_width);
-
-  child_alloc.x = -width_diff / 2;
-  child_alloc.y = 0;
-  child_alloc.height = height;
-  child_alloc.width = width + width_diff;
-
-  allocate_contents (self, &child_alloc, baseline);
-}
-
 static void
 adw_tab_map (GtkWidget *widget)
 {
@@ -664,8 +622,6 @@ adw_tab_class_init (AdwTabClass *klass)
   object_class->dispose = adw_tab_dispose;
   object_class->constructed = adw_tab_constructed;
 
-  widget_class->measure = adw_tab_measure;
-  widget_class->size_allocate = adw_tab_size_allocate;
   widget_class->map = adw_tab_map;
   widget_class->unmap = adw_tab_unmap;
   widget_class->snapshot = adw_tab_snapshot;
@@ -674,6 +630,8 @@ adw_tab_class_init (AdwTabClass *klass)
 
   item_class->connect_page = adw_tab_connect_page;
   item_class->disconnect_page = adw_tab_disconnect_page;
+  item_class->measure_contents = adw_tab_measure_contents;
+  item_class->allocate_contents = adw_tab_allocate_contents;
 
   gtk_widget_class_set_template_from_resource (widget_class,
                                                "/org/gnome/Adwaita/ui/adw-tab.ui");
diff --git a/src/adwaita.gresources.xml b/src/adwaita.gresources.xml
index e61cc635..12a594dd 100644
--- a/src/adwaita.gresources.xml
+++ b/src/adwaita.gresources.xml
@@ -3,6 +3,7 @@
   <gresource prefix="/org/gnome/Adwaita">
     <file>glsl/fade.glsl</file>
     <file>glsl/mask.glsl</file>
+    <file>glsl/tab-overview.glsl</file>
     <file preprocess="xml-stripblanks">icons/scalable/actions/adw-expander-arrow-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/avatar-default-symbolic.svg</file>
     <file preprocess="xml-stripblanks">icons/scalable/status/adw-tab-counter-symbolic.svg</file>
@@ -20,6 +21,8 @@
     <file preprocess="xml-stripblanks">adw-tab.ui</file>
     <file preprocess="xml-stripblanks">adw-tab-bar.ui</file>
     <file preprocess="xml-stripblanks">adw-tab-button.ui</file>
+    <file preprocess="xml-stripblanks">adw-tab-overview.ui</file>
+    <file preprocess="xml-stripblanks">adw-tab-thumbnail.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-bar.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-button.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-title.ui</file>
diff --git a/src/adwaita.h b/src/adwaita.h
index a9d71e30..178d8486 100644
--- a/src/adwaita.h
+++ b/src/adwaita.h
@@ -56,6 +56,7 @@ G_BEGIN_DECLS
 #include "adw-swipeable.h"
 #include "adw-tab-bar.h"
 #include "adw-tab-button.h"
+#include "adw-tab-overview.h"
 #include "adw-tab-view.h"
 #include "adw-view-stack.h"
 #include "adw-view-switcher.h"
diff --git a/src/glsl/tab-overview.glsl b/src/glsl/tab-overview.glsl
new file mode 100644
index 00000000..3c441d5f
--- /dev/null
+++ b/src/glsl/tab-overview.glsl
@@ -0,0 +1,18 @@
+uniform vec2 origin;
+uniform vec2 size;
+uniform float opacity;
+
+uniform sampler2D u_texture1;
+
+void
+mainImage(out vec4 fragColor,
+          in vec2  fragCoord,
+          in vec2  resolution,
+          in vec2  uv)
+{
+  if (fragCoord.x >= origin.x && fragCoord.x <= origin.x + size.x &&
+      fragCoord.y >= origin.y && fragCoord.y <= origin.y + size.y)
+    fragColor = vec4(0, 0, 0, 0);
+  else
+    fragColor = opacity * GskTexture(u_texture1, uv);
+}
diff --git a/src/meson.build b/src/meson.build
index 7e928669..ca362144 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -105,6 +105,7 @@ src_headers = [
   'adw-swipeable.h',
   'adw-tab-bar.h',
   'adw-tab-button.h',
+  'adw-tab-overview.h',
   'adw-tab-view.h',
   'adw-view-stack.h',
   'adw-view-switcher.h',
@@ -169,7 +170,11 @@ src_sources = [
   'adw-tab-bar.c',
   'adw-tab-box.c',
   'adw-tab-button.c',
+  'adw-tab-grid.c',
+  'adw-tab-item.c',
   'adw-tab-list-base.c',
+  'adw-tab-overview.c',
+  'adw-tab-thumbnail.c',
   'adw-tab-view.c',
   'adw-view-stack.c',
   'adw-view-switcher.c',
diff --git a/src/stylesheet/widgets/_tab-view.scss b/src/stylesheet/widgets/_tab-view.scss
index e227c677..bb09a8e3 100644
--- a/src/stylesheet/widgets/_tab-view.scss
+++ b/src/stylesheet/widgets/_tab-view.scss
@@ -182,3 +182,17 @@ tabview:drop(active),
 tabbox:drop(active) {
   box-shadow: none;
 }
+
+taboverview {
+  background: mix($bg_color, $fg_color, 85%);
+}
+
+tabthumbnail > box {
+  margin: 10px;
+
+  > picture {
+    background: $bg_color;
+    box-shadow: 0 0 0 1px $borders_color;
+    border-radius: 8px;
+  }
+}


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