[libadwaita/wip/exalm/tab-overview: 15/15] aaa a# Please enter the commit message for your changes. Lines starting
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libadwaita/wip/exalm/tab-overview: 15/15] aaa a# Please enter the commit message for your changes. Lines starting
- Date: Tue, 12 Oct 2021 08:51:33 +0000 (UTC)
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]