[epiphany] Add pinned tabs



commit 4be31e56e0fcecc7e2831fa470128fefa28dcab9
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Thu Mar 28 18:42:46 2019 +0100

    Add pinned tabs
    
    Fixes: https://gitlab.gnome.org/GNOME/epiphany/issues/248

 src/ephy-notebook.c                        | 68 +++++++++++++++++++++++++++++-
 src/ephy-notebook.h                        |  5 +++
 src/ephy-session.c                         | 25 +++++++++--
 src/ephy-tab-label.c                       | 34 +++++++++++++++
 src/ephy-tab-label.h                       |  3 ++
 src/ephy-window.c                          | 19 ++++++++-
 src/resources/gtk/notebook-context-menu.ui | 10 +++++
 src/window-commands.c                      | 40 +++++++++++++++++-
 src/window-commands.h                      |  6 +++
 9 files changed, 204 insertions(+), 6 deletions(-)
---
diff --git a/src/ephy-notebook.c b/src/ephy-notebook.c
index fa1c0c0fe..78c1c846d 100644
--- a/src/ephy-notebook.c
+++ b/src/ephy-notebook.c
@@ -388,9 +388,10 @@ expand_tabs_changed_cb (GSettings    *settings,
   tabs = gtk_container_get_children (GTK_CONTAINER (nb));
 
   for (l = tabs; l != NULL; l = l->next) {
+    gboolean pinned = ephy_notebook_tab_is_pinned (nb, l->data);
     gtk_container_child_set (GTK_CONTAINER (nb),
                              l->data,
-                             "tab-expand", expand,
+                             "tab-expand", pinned ? FALSE : expand,
                              NULL);
   }
 
@@ -465,6 +466,36 @@ show_tabs_changed_cb (GSettings    *settings,
   update_tabs_visibility (nb, FALSE);
 }
 
+static guint
+get_last_pinned_tab_pos (GtkNotebook *notebook)
+{
+  gint pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
+  gint i;
+  gint found = -1;
+
+  for (i = 0; i < pages; i++) {
+    GtkWidget *child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+
+    if (ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), EPHY_EMBED (child)))
+      found = i;
+  }
+
+  return found;
+}
+
+static void
+page_reordered_cb (GtkNotebook *notebook,
+                   GtkWidget   *child,
+                   guint        page_num,
+                   gpointer     user_data)
+{
+  guint last_pinned_tab_pos = get_last_pinned_tab_pos (notebook);
+
+  /* Ensure that pinned tabs will always stay at the beginning of tab bar */
+  if (last_pinned_tab_pos != -1 && page_num <= last_pinned_tab_pos)
+    gtk_notebook_reorder_child (notebook, child, last_pinned_tab_pos);
+}
+
 static void
 ephy_notebook_init (EphyNotebook *notebook)
 {
@@ -484,6 +515,8 @@ ephy_notebook_init (EphyNotebook *notebook)
   g_signal_connect_after (notebook, "switch-page",
                           G_CALLBACK (ephy_notebook_switch_page_cb),
                           NULL);
+  g_signal_connect (notebook, "page-reordered",
+                    (GCallback)page_reordered_cb, NULL);
 
   /* Set up drag-and-drop target */
   g_signal_connect (notebook, "drag-data-received",
@@ -922,3 +955,36 @@ ephy_notebook_set_adaptive_mode (EphyNotebook     *notebook,
   notebook->adaptive_mode = adaptive_mode;
   update_tabs_visibility (notebook, FALSE);
 }
+
+void
+ephy_notebook_tab_set_pinned (EphyNotebook *notebook,
+                              GtkWidget    *embed,
+                              gboolean      is_pinned)
+{
+  GtkWidget *tab_label;
+  gboolean expanded;
+
+  if (is_pinned) {
+    gtk_notebook_reorder_child (GTK_NOTEBOOK (notebook), embed, 0);
+    expanded = FALSE;
+  } else {
+    expanded = g_settings_get_boolean (EPHY_SETTINGS_UI, EPHY_PREFS_UI_EXPAND_TABS_BAR);
+    page_reordered_cb (GTK_NOTEBOOK (notebook), embed, 0, NULL);
+  }
+
+  gtk_container_child_set (GTK_CONTAINER (notebook), embed, "tab-expand", expanded, NULL);
+
+  gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (notebook), embed, !is_pinned);
+
+  tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), embed);
+  ephy_tab_label_set_pin (tab_label, is_pinned);
+}
+
+gboolean
+ephy_notebook_tab_is_pinned (EphyNotebook *notebook,
+                             EphyEmbed    *embed)
+{
+  GtkWidget *tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), GTK_WIDGET (embed));
+
+  return ephy_tab_label_get_pin (tab_label);
+}
diff --git a/src/ephy-notebook.h b/src/ephy-notebook.h
index 39fe6a72d..a54b50f83 100644
--- a/src/ephy-notebook.h
+++ b/src/ephy-notebook.h
@@ -51,4 +51,9 @@ GMenu          *ephy_notebook_get_pages_menu    (EphyNotebook *notebook);
 void            ephy_notebook_set_adaptive_mode (EphyNotebook     *notebook,
                                                  EphyAdaptiveMode  adaptive_mode);
 
+void           ephy_notebook_tab_set_pinned    (EphyNotebook *notebook,
+                                                GtkWidget    *embed,
+                                                gboolean      is_pinned);
+gboolean       ephy_notebook_tab_is_pinned     (EphyNotebook *notebook,
+                                                EphyEmbed    *embed);
 G_END_DECLS
diff --git a/src/ephy-session.c b/src/ephy-session.c
index ed8fb3502..49212b6b1 100644
--- a/src/ephy-session.c
+++ b/src/ephy-session.c
@@ -545,12 +545,14 @@ typedef struct {
   char *title;
   gboolean loading;
   gboolean crashed;
+  gboolean pinned;
   WebKitWebViewSessionState *state;
 } SessionTab;
 
 static SessionTab *
 session_tab_new (EphyEmbed   *embed,
-                 EphySession *session)
+                 EphySession *session,
+                 GtkNotebook *notebook)
 {
   SessionTab *session_tab;
   const char *address;
@@ -579,6 +581,7 @@ session_tab_new (EphyEmbed   *embed,
   session_tab->crashed = (error_page == EPHY_WEB_VIEW_ERROR_PAGE_CRASH ||
                           error_page == EPHY_WEB_VIEW_ERROR_PROCESS_CRASH);
   session_tab->state = webkit_web_view_get_session_state (WEBKIT_WEB_VIEW (web_view));
+  session_tab->pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed);
 
   return session_tab;
 }
@@ -620,17 +623,17 @@ session_window_new (EphyWindow  *window,
   session_window = g_new0 (SessionWindow, 1);
   get_window_geometry (GTK_WINDOW (window), &session_window->geometry);
   session_window->role = g_strdup (gtk_window_get_role (GTK_WINDOW (window)));
+  notebook = GTK_NOTEBOOK (ephy_window_get_notebook (window));
 
   for (l = tabs; l != NULL; l = l->next) {
     SessionTab *tab;
 
-    tab = session_tab_new (EPHY_EMBED (l->data), session);
+    tab = session_tab_new (EPHY_EMBED (l->data), session, notebook);
     session_window->tabs = g_list_prepend (session_window->tabs, tab);
   }
   g_list_free (tabs);
   session_window->tabs = g_list_reverse (session_window->tabs);
 
-  notebook = GTK_NOTEBOOK (ephy_window_get_notebook (window));
   session_window->active_tab = gtk_notebook_get_current_page (notebook);
 
   return session_window;
@@ -712,6 +715,14 @@ write_tab (xmlTextWriterPtr writer,
       return ret;
   }
 
+  if (tab->pinned) {
+    ret = xmlTextWriterWriteAttribute (writer,
+                                       (const xmlChar *)"pinned",
+                                       (const xmlChar *)"true");
+    if (ret < 0)
+      return ret;
+  }
+
   if (tab->crashed) {
     ret = xmlTextWriterWriteAttribute (writer,
                                        (const xmlChar *)"crashed",
@@ -1125,14 +1136,18 @@ session_parse_embed (SessionParserContext *context,
                      const gchar         **names,
                      const gchar         **values)
 {
+  GtkWidget *notebook;
   const char *url = NULL;
   const char *title = NULL;
   const char *history = NULL;
   gboolean was_loading = FALSE;
   gboolean crashed = FALSE;
   gboolean is_blank_page = FALSE;
+  gboolean is_pin = FALSE;
   guint i;
 
+  notebook = ephy_window_get_notebook (context->window);
+
   for (i = 0; names[i]; i++) {
     if (strcmp (names[i], "url") == 0) {
       url = values[i];
@@ -1146,6 +1161,8 @@ session_parse_embed (SessionParserContext *context,
       crashed = strcmp (values[i], "true") == 0;
     } else if (strcmp (names[i], "history") == 0) {
       history = values[i];
+    } else if (strcmp (names[i], "pinned") == 0) {
+      is_pin = strcmp (values[i], "true") == 0;
     }
   }
 
@@ -1179,6 +1196,8 @@ session_parse_embed (SessionParserContext *context,
                                      context->window, NULL, flags,
                                      0);
 
+    ephy_notebook_tab_set_pinned (EPHY_NOTEBOOK (notebook), GTK_WIDGET (embed), is_pin);
+
     web_view = ephy_embed_get_web_view (embed);
     if (history) {
       guchar *data;
diff --git a/src/ephy-tab-label.c b/src/ephy-tab-label.c
index eff6b01d9..bbc11f429 100644
--- a/src/ephy-tab-label.c
+++ b/src/ephy-tab-label.c
@@ -33,6 +33,8 @@ struct _EphyTabLabel {
   GtkWidget *label;
   GtkWidget *close_button;
   GtkWidget *audio_button;
+
+  gboolean pinned;
 };
 
 enum {
@@ -144,6 +146,11 @@ style_updated_cb (GtkWidget *widget,
   EphyTabLabel *self = EPHY_TAB_LABEL (widget);
   int char_width, h, w;
 
+  if (self->pinned) {
+    gtk_widget_set_size_request (widget, -1, -1);
+    return;
+  }
+
   context = gtk_widget_get_pango_context (widget);
   style = gtk_widget_get_style_context (widget);
   gtk_style_context_get (style, gtk_style_context_get_state (style), "font", &font_desc, NULL);
@@ -230,3 +237,30 @@ ephy_tab_label_get_text (GtkWidget *widget)
 
   return gtk_label_get_text (GTK_LABEL (self->label));
 }
+
+static void
+update_label (EphyTabLabel *self)
+{
+  gtk_widget_set_visible (self->close_button, !self->pinned);
+  gtk_widget_set_visible (self->label, !self->pinned);
+  gtk_widget_set_halign (GTK_WIDGET (self), self->pinned ? GTK_ALIGN_CENTER : GTK_ALIGN_FILL);
+  g_signal_emit_by_name (self, "style-updated", G_TYPE_NONE);
+}
+
+void
+ephy_tab_label_set_pin (GtkWidget *widget,
+                        gboolean   pinned)
+{
+  EphyTabLabel *self = EPHY_TAB_LABEL (widget);
+
+  self->pinned = pinned;
+  update_label (self);
+}
+
+gboolean
+ephy_tab_label_get_pin (GtkWidget *widget)
+{
+  EphyTabLabel *self = EPHY_TAB_LABEL (widget);
+
+  return self->pinned;
+}
diff --git a/src/ephy-tab-label.h b/src/ephy-tab-label.h
index 86cb7f63b..8e299050a 100644
--- a/src/ephy-tab-label.h
+++ b/src/ephy-tab-label.h
@@ -32,5 +32,8 @@ G_DECLARE_FINAL_TYPE (EphyTabLabel, ephy_tab_label, EPHY, TAB_LABEL, GtkBox);
 
 GtkWidget   *ephy_tab_label_new      (void);
 const gchar *ephy_tab_label_get_text (GtkWidget *self);
+void         ephy_tab_label_set_pin  (GtkWidget *self,
+                                      gboolean pin);
+gboolean     ephy_tab_label_get_pin  (GtkWidget *self);
 
 G_END_DECLS
diff --git a/src/ephy-window.c b/src/ephy-window.c
index ca161a4ff..b694fc382 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -128,7 +128,8 @@ const struct {
   { "tab.move-left", { "<shift><Primary>Page_Up", "<shift><Primary>Page_Up", NULL } },
   { "tab.move-right", { "<shift><Primary>Page_Down", "<shift><Primary>Page_Down", NULL } },
   { "tab.duplicate", { NULL } },
-  { "tab.close", { "<Primary>W", NULL } }
+  { "tab.close", { "<Primary>W", NULL } },
+  { "tab.pin", { NULL } }
 }, accels_navigation_ltr [] = {
   { "toolbar.navigation-back", { "<alt>Left", "<alt>KP_Left", "<alt>KP_4", "Back", NULL } },
   { "toolbar.navigation-forward", { "<alt>Right", "<alt>KP_Right", "<alt>KP_6", "Forward", NULL } }
@@ -853,6 +854,8 @@ static const GActionEntry tab_entries [] = {
   { "reload", window_cmd_tabs_reload },
   { "reopen", window_cmd_tabs_reopen_closed_tab },
   { "reload-all", window_cmd_tabs_reload_all_tabs },
+  { "pin", window_cmd_tabs_pin },
+  { "unpin", window_cmd_tabs_unpin },
 };
 
 static const GActionEntry toolbar_entries [] = {
@@ -2518,6 +2521,7 @@ show_notebook_popup_menu (GtkNotebook    *notebook,
   if (event != NULL) {
     int n_pages;
     int page_num;
+    gboolean pinned;
 
     tab = GTK_WIDGET (window->active_embed);
     n_pages = gtk_notebook_get_n_pages (notebook);
@@ -2540,6 +2544,19 @@ show_notebook_popup_menu (GtkNotebook    *notebook,
                                          "reload-all");
     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), n_pages > 1);
 
+    pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), EPHY_EMBED (tab));
+    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                         "pin");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !pinned);
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                         "unpin");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), pinned);
+
+    action = g_action_map_lookup_action (G_ACTION_MAP (action_group),
+                                         "close-tab");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !pinned);
+
     gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event);
   } else {
     tab = GTK_WIDGET (window->active_embed);
diff --git a/src/resources/gtk/notebook-context-menu.ui b/src/resources/gtk/notebook-context-menu.ui
index f01310cd5..050374bfa 100644
--- a/src/resources/gtk/notebook-context-menu.ui
+++ b/src/resources/gtk/notebook-context-menu.ui
@@ -16,6 +16,16 @@
         <attribute name="action">tab.duplicate</attribute>
       </item>
     </section>
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">P_in Tab</attribute>
+        <attribute name="action">tab.pin</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">Unpi_n Tab</attribute>
+        <attribute name="action">tab.unpin</attribute>
+      </item>
+    </section>
     <section>
       <item>
         <attribute name="label" translatable="yes">Reo_pen Closed Tab</attribute>
diff --git a/src/window-commands.c b/src/window-commands.c
index bc5bb99fd..95bda9585 100644
--- a/src/window-commands.c
+++ b/src/window-commands.c
@@ -2474,6 +2474,7 @@ window_cmd_tabs_close (GSimpleAction *action,
   EphyWindow *window = user_data;
   GtkWidget *notebook;
   EphyEmbed *embed;
+  gboolean pinned;
 
   notebook = ephy_window_get_notebook (window);
 
@@ -2486,7 +2487,10 @@ window_cmd_tabs_close (GSimpleAction *action,
   embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
   g_assert (embed != NULL);
 
-  g_signal_emit_by_name (notebook, "tab-close-request", embed);
+  pinned = ephy_notebook_tab_is_pinned (EPHY_NOTEBOOK (notebook), embed);
+
+  if (!pinned)
+    g_signal_emit_by_name (notebook, "tab-close-request", embed);
 }
 
 void
@@ -2753,3 +2757,37 @@ window_cmd_new_tab_from_clipboard (GSimpleAction *action,
                               (GtkClipboardTextReceivedFunc)clipboard_text_received_cb,
                               g_object_ref (ephy_window));
 }
+
+void
+window_cmd_tabs_pin (GSimpleAction *action,
+                     GVariant      *parameter,
+                     gpointer       user_data)
+{
+  EphyWindow *window = EPHY_WINDOW (user_data);
+  EphyEmbed *embed;
+  EphyNotebook *notebook;
+
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+  g_assert (embed != NULL);
+
+  notebook = EPHY_NOTEBOOK (ephy_window_get_notebook (window));
+
+  ephy_notebook_tab_set_pinned (notebook, GTK_WIDGET (embed), TRUE);
+}
+
+void
+window_cmd_tabs_unpin (GSimpleAction *action,
+                       GVariant      *parameter,
+                       gpointer       user_data)
+{
+  EphyWindow *window = EPHY_WINDOW (user_data);
+  EphyEmbed *embed;
+  EphyNotebook *notebook;
+
+  embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
+  g_assert (embed != NULL);
+
+  notebook = EPHY_NOTEBOOK (ephy_window_get_notebook (window));
+
+  ephy_notebook_tab_set_pinned (notebook, GTK_WIDGET (embed), FALSE);
+}
diff --git a/src/window-commands.h b/src/window-commands.h
index b60495ab8..3f2f6cbff 100644
--- a/src/window-commands.h
+++ b/src/window-commands.h
@@ -221,5 +221,11 @@ void window_cmd_homepage_new_tab                (GSimpleAction *action,
 void window_cmd_new_tab_from_clipboard          (GSimpleAction *action,
                                                  GVariant      *parameter,
                                                  gpointer       user_data);
+void window_cmd_tabs_pin                        (GSimpleAction *action,
+                                                 GVariant      *parameter,
+                                                 gpointer       user_data);
+void window_cmd_tabs_unpin                      (GSimpleAction *action,
+                                                 GVariant      *parameter,
+                                                 gpointer       user_data);
 
 G_END_DECLS


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