[gnome-builder/editor-layout] tabs: more wip on tabs.



commit bf835348fd9332c98fe0b50a250b68fa217461a1
Author: Christian Hergert <christian hergert me>
Date:   Tue Nov 25 18:01:57 2014 -0800

    tabs: more wip on tabs.

 src/resources/keybindings/default.ini |    8 ++
 src/tabs/gb-tab-grid.c                |  156 ++++++++++++++++++++++++++++++++-
 src/tabs/gb-tab-stack.c               |   44 ++++++++--
 src/tabs/gb-tab-stack.h               |    1 +
 4 files changed, 202 insertions(+), 7 deletions(-)
---
diff --git a/src/resources/keybindings/default.ini b/src/resources/keybindings/default.ini
index f3faae5..65fb4ef 100644
--- a/src/resources/keybindings/default.ini
+++ b/src/resources/keybindings/default.ini
@@ -30,3 +30,11 @@ save-as = <Control><Shift>S
 toggle-preview = <Control><Alt>P
 scroll-up = <Control>Y
 scroll-down = <Control>E
+
+[tabs]
+next = <Control>J
+previous = <Control>K
+left = <Control>H
+right = <Control>L
+move-left = <Control><Shift>H
+move-right = <Control><Shift>L
diff --git a/src/tabs/gb-tab-grid.c b/src/tabs/gb-tab-grid.c
index 720225a..bea0605 100644
--- a/src/tabs/gb-tab-grid.c
+++ b/src/tabs/gb-tab-grid.c
@@ -27,7 +27,10 @@
 
 struct _GbTabGridPrivate
 {
-  GtkWidget *top_hpaned;
+  GSimpleActionGroup *actions;
+
+  GtkWidget          *top_hpaned;
+  GbTabStack         *last_focused_stack;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GbTabGrid, gb_tab_grid, GTK_TYPE_BIN)
@@ -35,6 +38,40 @@ G_DEFINE_TYPE_WITH_PRIVATE (GbTabGrid, gb_tab_grid, GTK_TYPE_BIN)
 static GtkWidget *
 gb_tab_grid_get_first_stack (GbTabGrid*);
 
+static GbTabStack *
+gb_tab_grid_get_last_focused (GbTabGrid *grid)
+{
+  g_return_val_if_fail (GB_IS_TAB_GRID (grid), NULL);
+
+  return grid->priv->last_focused_stack;
+}
+
+static void
+gb_tab_grid_set_last_focused (GbTabGrid  *grid,
+                              GbTabStack *stack)
+{
+  GbTabGridPrivate *priv;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+  g_return_if_fail (!stack || GB_TAB_STACK (stack));
+
+  priv = grid->priv;
+
+  if (priv->last_focused_stack)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (priv->last_focused_stack),
+                                    (gpointer *)&priv->last_focused_stack);
+      priv->last_focused_stack = NULL;
+    }
+
+  if (stack)
+    {
+      priv->last_focused_stack = stack;
+      g_object_add_weak_pointer (G_OBJECT (stack),
+                                 (gpointer *)&priv->last_focused_stack);
+    }
+}
+
 GtkWidget *
 gb_tab_grid_new (void)
 {
@@ -387,6 +424,109 @@ gb_tab_grid_focus_previous_view (GbTabGrid *self,
   EXIT;
 }
 
+static void
+on_next_tab (GSimpleAction *action,
+             GVariant      *parameters,
+             gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  if (last_focused_stack)
+    if (!gb_tab_stack_focus_next (last_focused_stack))
+      gb_tab_stack_focus_first  (last_focused_stack);
+
+  EXIT;
+}
+
+static void
+on_previous_tab (GSimpleAction *action,
+                 GVariant      *parameters,
+                 gpointer       user_data)
+{
+  GbTabGrid *self = user_data;
+  GbTabStack *last_focused_stack = NULL;
+
+  ENTRY;
+
+  g_return_if_fail (GB_IS_TAB_GRID (self));
+
+  last_focused_stack = gb_tab_grid_get_last_focused (self);
+  if (last_focused_stack)
+    if (!gb_tab_stack_focus_previous (last_focused_stack))
+      gb_tab_stack_focus_last (last_focused_stack);
+
+  EXIT;
+}
+
+static void
+gb_tab_grid_on_set_focus (GbTabGrid *grid,
+                          GtkWidget *widget,
+                          GtkWindow *window)
+{
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  if (widget)
+    {
+      while (widget && !GB_IS_TAB_STACK (widget))
+        widget = gtk_widget_get_parent (widget);
+
+      if (GB_IS_TAB_STACK (widget))
+        gb_tab_grid_set_last_focused (grid, GB_TAB_STACK (widget));
+    }
+}
+
+static void
+gb_tab_grid_realize (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+  GbTabGrid *grid = (GbTabGrid *)widget;
+
+  g_return_if_fail (GB_IS_TAB_GRID (grid));
+
+  GTK_WIDGET_CLASS (gb_tab_grid_parent_class)->realize (widget);
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (GTK_IS_WINDOW (toplevel))
+    {
+      /*
+       * WORKAROUND:
+       *
+       * We need to register our actions with the toplevel or they wont be
+       * taken into account when activating accelerators. See bugzilla bug
+       * 740682 for a patch to Gtk+.
+       */
+      gtk_widget_insert_action_group (toplevel, "tabs",
+                                      G_ACTION_GROUP (grid->priv->actions));
+
+      /*
+       * Track focus so we know where we are moving to/from in the stack.
+       */
+      g_signal_connect_object (toplevel,
+                               "set-focus",
+                               G_CALLBACK (gb_tab_grid_on_set_focus),
+                               widget,
+                               G_CONNECT_SWAPPED);
+    }
+}
+
+static void
+gb_tab_grid_finalize (GObject *object)
+{
+  GbTabGridPrivate *priv = GB_TAB_GRID (object)->priv;
+
+  g_clear_object (&priv->actions);
+
+  G_OBJECT_CLASS (gb_tab_grid_parent_class)->finalize (object);
+}
+
 /**
  * gb_tab_grid_class_init:
  * @klass: (in): A #GbTabGridClass.
@@ -396,8 +536,14 @@ gb_tab_grid_focus_previous_view (GbTabGrid *self,
 static void
 gb_tab_grid_class_init (GbTabGridClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
+  object_class->finalize = gb_tab_grid_finalize;
+
+  widget_class->realize = gb_tab_grid_realize;
+
   container_class->add = gb_tab_grid_add;
 }
 
@@ -410,11 +556,19 @@ gb_tab_grid_class_init (GbTabGridClass *klass)
 static void
 gb_tab_grid_init (GbTabGrid *self)
 {
+  static const GActionEntry entries[] = {
+    { "next", on_next_tab },
+    { "previous", on_previous_tab },
+  };
   GtkWidget *paned;
   GtkWidget *stack;
 
   self->priv = gb_tab_grid_get_instance_private (self);
 
+  self->priv->actions = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (self->priv->actions),
+                                   entries, G_N_ELEMENTS (entries), self);
+
   self->priv->top_hpaned =
     g_object_new (GTK_TYPE_PANED,
                   "orientation", GTK_ORIENTATION_HORIZONTAL,
diff --git a/src/tabs/gb-tab-stack.c b/src/tabs/gb-tab-stack.c
index 3ccc553..fc47544 100644
--- a/src/tabs/gb-tab-stack.c
+++ b/src/tabs/gb-tab-stack.c
@@ -16,8 +16,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "tab-stack"
+
 #include <glib/gi18n.h>
 
+#include "gb-log.h"
 #include "gb-tab-stack.h"
 
 struct _GbTabStackPrivate
@@ -163,16 +166,18 @@ gb_tab_stack_focus_next (GbTabStack *stack)
   GtkTreeIter iter;
   gboolean ret = FALSE;
 
+  ENTRY;
+
   g_return_val_if_fail (GB_IS_TAB_STACK (stack), FALSE);
 
   if (!(child = gtk_stack_get_visible_child (stack->priv->stack)))
-    return FALSE;
+    RETURN (FALSE);
 
   if (gb_tab_stack_get_tab_iter (stack, GB_TAB (child), &iter) &&
       gtk_tree_model_iter_next (GTK_TREE_MODEL (stack->priv->store), &iter))
     ret = gb_tab_stack_focus_iter (stack, &iter);
 
-  return ret;
+  RETURN (ret);
 }
 
 gboolean
@@ -182,16 +187,18 @@ gb_tab_stack_focus_previous (GbTabStack *stack)
   GtkTreeIter iter;
   gboolean ret = FALSE;
 
+  ENTRY;
+
   g_return_val_if_fail (GB_IS_TAB_STACK (stack), FALSE);
 
   if (!(child = gtk_stack_get_visible_child (stack->priv->stack)))
-    return FALSE;
+    RETURN (FALSE);
 
   if (gb_tab_stack_get_tab_iter (stack, GB_TAB (child), &iter) &&
       gtk_tree_model_iter_previous (GTK_TREE_MODEL (stack->priv->store), &iter))
     ret = gb_tab_stack_focus_iter (stack, &iter);
 
-  return ret;
+  RETURN (ret);
 }
 
 gboolean
@@ -199,13 +206,38 @@ gb_tab_stack_focus_first (GbTabStack *stack)
 {
   GtkTreeIter iter;
 
+  ENTRY;
+
   g_return_val_if_fail (GB_IS_TAB_STACK (stack), FALSE);
 
   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (stack->priv->store),
                                      &iter))
-    return gb_tab_stack_focus_iter (stack, &iter);
+    RETURN (gb_tab_stack_focus_iter (stack, &iter));
 
-  return FALSE;
+  RETURN (FALSE);
+}
+
+gboolean
+gb_tab_stack_focus_last (GbTabStack *stack)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  guint n_children;
+
+  ENTRY;
+
+  g_return_val_if_fail (GB_IS_TAB_STACK (stack), FALSE);
+
+  model = GTK_TREE_MODEL (stack->priv->store);
+  n_children = gtk_tree_model_iter_n_children (model, NULL);
+
+  if (n_children != 0)
+    {
+      if (gtk_tree_model_iter_nth_child (model, &iter, NULL, n_children-1))
+        RETURN (gb_tab_stack_focus_iter (stack, &iter));
+    }
+
+  RETURN (FALSE);
 }
 
 gboolean
diff --git a/src/tabs/gb-tab-stack.h b/src/tabs/gb-tab-stack.h
index 6d13fa5..1779dbf 100644
--- a/src/tabs/gb-tab-stack.h
+++ b/src/tabs/gb-tab-stack.h
@@ -58,6 +58,7 @@ void       gb_tab_stack_remove_tab     (GbTabStack *stack,
                                         GbTab      *tab);
 guint      gb_tab_stack_get_n_tabs     (GbTabStack *stack);
 gboolean   gb_tab_stack_focus_first    (GbTabStack *stack);
+gboolean   gb_tab_stack_focus_last     (GbTabStack *stack);
 gboolean   gb_tab_stack_focus_next     (GbTabStack *stack);
 gboolean   gb_tab_stack_focus_previous (GbTabStack *stack);
 gboolean   gb_tab_stack_focus_tab      (GbTabStack *stack,


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