[gtk+/wip/matthiasc/tab-strip: 10/10] tab strip: Support different edges



commit 1525f4b2e7ed1fda36837edab3cca0c4c39251f1
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat May 21 16:13:34 2016 -0400

    tab strip: Support different edges
    
    Add edge properties on both GtkTabStrip and GtkTab, and make
    GtkTabStrip update its orientation and scrolling direction when
    the edge changes.

 gtk/gtktab.c      |   43 ++++++++++++++
 gtk/gtktab.h      |    5 ++
 gtk/gtktabstrip.c |  159 ++++++++++++++++++++++++++++++++++++++++++++++++----
 gtk/gtktabstrip.h |    5 ++
 4 files changed, 199 insertions(+), 13 deletions(-)
---
diff --git a/gtk/gtktab.c b/gtk/gtktab.c
index 6a0bf20..36946f1 100644
--- a/gtk/gtktab.c
+++ b/gtk/gtktab.c
@@ -32,6 +32,7 @@ struct _GtkTabPrivate
 {
   gchar           *title;
   GtkWidget       *widget;
+  GtkPositionType  edge;
 
   GtkWidget       *child;
   GtkCssGadget    *gadget;
@@ -44,6 +45,7 @@ enum {
   PROP_0,
   PROP_TITLE,
   PROP_WIDGET,
+  PROP_EDGE,
   N_PROPS
 };
 
@@ -74,6 +76,10 @@ gtk_tab_get_property (GObject    *object,
       g_value_set_object (value, gtk_tab_get_widget (self));
       break;
 
+    case PROP_EDGE:
+      g_value_set_enum (value, gtk_tab_get_edge (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -97,6 +103,10 @@ gtk_tab_set_property (GObject      *object,
       gtk_tab_set_widget (self, g_value_get_object (value));
       break;
 
+    case PROP_EDGE:
+      gtk_tab_set_edge (self, g_value_get_enum (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -423,6 +433,12 @@ gtk_tab_class_init (GtkTabClass *klass)
                          GTK_TYPE_WIDGET,
                          GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
+  properties [PROP_EDGE] =
+    g_param_spec_enum ("edge", P_("Edge"), P_("The edge for the tab-strip"),
+                       GTK_TYPE_POSITION_TYPE,
+                       GTK_POS_TOP,
+                       GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
   signals[ACTIVATE] =
@@ -450,6 +466,8 @@ gtk_tab_init (GtkTab *self)
   widget_node = gtk_widget_get_css_node (GTK_WIDGET (self));
   priv->gadget = gtk_box_gadget_new_for_node (widget_node, GTK_WIDGET (self));
   gtk_box_gadget_set_draw_focus (GTK_BOX_GADGET (priv->gadget), TRUE);
+
+  priv->edge = GTK_POS_TOP;
 }
 
 const gchar *
@@ -517,3 +535,28 @@ gtk_tab_set_child (GtkTab    *self,
 
   gtk_tab_add (GTK_CONTAINER (self), child);
 }
+
+GtkPositionType
+gtk_tab_get_edge (GtkTab *self)
+{
+  GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
+
+  g_return_val_if_fail (GTK_IS_TAB (self), GTK_POS_TOP);
+
+  return priv->edge;
+}
+
+void
+gtk_tab_set_edge (GtkTab          *self,
+                  GtkPositionType  edge)
+{
+  GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
+
+  g_return_if_fail (GTK_IS_TAB (self));
+
+  if (priv->edge == edge)
+    return;
+
+  priv->edge = edge;
+  g_object_notify (G_OBJECT (self), "edge");
+}
diff --git a/gtk/gtktab.h b/gtk/gtktab.h
index af5741e..1c1d18a 100644
--- a/gtk/gtktab.h
+++ b/gtk/gtktab.h
@@ -62,6 +62,11 @@ GDK_AVAILABLE_IN_3_22
 void             gtk_tab_set_title  (GtkTab          *self,
                                      const gchar     *title);
 GDK_AVAILABLE_IN_3_22
+GtkPositionType  gtk_tab_get_edge   (GtkTab          *self);
+GDK_AVAILABLE_IN_3_22
+void             gtk_tab_set_edge   (GtkTab          *self,
+                                     GtkPositionType  edge);
+GDK_AVAILABLE_IN_3_22
 GtkWidget       *gtk_tab_get_widget (GtkTab          *self);
 GDK_AVAILABLE_IN_3_22
 void             gtk_tab_set_widget (GtkTab          *self,
diff --git a/gtk/gtktabstrip.c b/gtk/gtktabstrip.c
index 67c16b6..25030f9 100644
--- a/gtk/gtktabstrip.c
+++ b/gtk/gtktabstrip.c
@@ -33,12 +33,12 @@
  * TODO:
  * - reordering
  * - dnd
- * - other edges
  */
 
 typedef struct
 {
   GtkStack        *stack;
+  GtkPositionType  edge;
   gboolean         closable;
   gboolean         scrollable;
   gboolean         in_child_changed;
@@ -55,6 +55,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkTabStrip, gtk_tab_strip, GTK_TYPE_BOX)
 enum {
   PROP_0,
   PROP_STACK,
+  PROP_EDGE,
   PROP_CLOSABLE,
   PROP_SCROLLABLE,
   N_PROPS
@@ -103,6 +104,10 @@ gtk_tab_strip_get_property (GObject    *object,
       g_value_set_object (value, gtk_tab_strip_get_stack (self));
       break;
 
+    case PROP_EDGE:
+      g_value_set_enum (value, gtk_tab_strip_get_edge (self));
+      break;
+
     case PROP_CLOSABLE:
       g_value_set_boolean (value, gtk_tab_strip_get_closable (self));
       break;
@@ -130,6 +135,10 @@ gtk_tab_strip_set_property (GObject      *object,
       gtk_tab_strip_set_stack (self, g_value_get_object (value));
       break;
 
+    case PROP_EDGE:
+      gtk_tab_strip_set_edge (self, g_value_get_enum (value));
+      break;
+
     case PROP_CLOSABLE:
       gtk_tab_strip_set_closable (self, g_value_get_boolean (value));
       break;
@@ -177,6 +186,12 @@ gtk_tab_strip_class_init (GtkTabStripClass *klass)
                          GTK_TYPE_STACK,
                          GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
 
+  properties[PROP_EDGE] =
+    g_param_spec_enum ("edge", P_("Edge"), P_("The edge for the tab-strip"),
+                       GTK_TYPE_POSITION_TYPE,
+                       GTK_POS_TOP,
+                       GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
   properties[PROP_CLOSABLE] =
     g_param_spec_boolean ("closable", P_("Closable"), P_("Whether tabs can be closed"),
                           FALSE,
@@ -191,6 +206,25 @@ gtk_tab_strip_class_init (GtkTabStripClass *klass)
   gtk_widget_class_set_css_name (widget_class, "tabs");
 }
 
+static void
+update_scrolling (GtkTabStrip *self)
+{
+  GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
+  GtkPolicyType hscroll = GTK_POLICY_NEVER;
+  GtkPolicyType vscroll = GTK_POLICY_NEVER;
+
+  if (priv->scrollable)
+    {
+      if (gtk_orientable_get_orientation (GTK_ORIENTABLE (self)) == GTK_ORIENTATION_HORIZONTAL)
+        hscroll = GTK_POLICY_EXTERNAL;
+      else
+        vscroll = GTK_POLICY_EXTERNAL;
+    }
+
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
+                                  hscroll, vscroll);
+}
+
 static gboolean
 autoscroll_cb (GtkWidget     *widget,
                GdkFrameClock *frame_clock,
@@ -201,7 +235,10 @@ autoscroll_cb (GtkWidget     *widget,
   GtkAdjustment *adj;
   gdouble value;
 
-  adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (self)) == GTK_ORIENTATION_HORIZONTAL)
+    adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  else
+    adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
 
   value = gtk_adjustment_get_value (adj);
   if (priv->autoscroll_mode == GTK_SCROLL_STEP_FORWARD)
@@ -265,7 +302,10 @@ scroll_button_activate (GtkWidget   *button,
   GtkAdjustment *adj;
   gdouble value;
 
-  adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (self)) == GTK_ORIENTATION_HORIZONTAL)
+    adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  else
+    adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
   value = gtk_adjustment_get_value (adj);
 
   if (priv->start_scroll == button)
@@ -277,13 +317,18 @@ scroll_button_activate (GtkWidget   *button,
 }
 
 static void
-adjustment_changed (GtkAdjustment *adj,
-                    GtkTabStrip   *self)
+adjustment_changed (GtkTabStrip *self)
 {
   GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
+  GtkAdjustment *adj;
   gdouble value;
   gboolean at_lower, at_upper;
 
+  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (self)) == GTK_ORIENTATION_HORIZONTAL)
+    adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  else
+    adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+
   value = gtk_adjustment_get_value (adj);
 
   at_lower = value <= gtk_adjustment_get_lower (adj);
@@ -301,6 +346,10 @@ gtk_tab_strip_init (GtkTabStrip *self)
   GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
   GtkAdjustment *adj;
 
+  priv->edge = GTK_POS_TOP;
+  priv->scrollable = FALSE;
+  priv->closable = FALSE;
+
   priv->start_scroll = gtk_button_new_from_icon_name ("pan-start-symbolic", GTK_ICON_SIZE_MENU);
   gtk_button_set_relief (GTK_BUTTON (priv->start_scroll), GTK_RELIEF_NONE);
   gtk_widget_show (priv->start_scroll);
@@ -315,12 +364,11 @@ gtk_tab_strip_init (GtkTabStrip *self)
                     G_CALLBACK (scroll_button_activate), self);
 
   priv->scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
-                                  GTK_POLICY_NEVER,
-                                  GTK_POLICY_NEVER);
   gtk_widget_show (priv->scrolledwindow);
   gtk_box_pack_start (GTK_BOX (self), priv->scrolledwindow, TRUE, TRUE, 0);
 
+  update_scrolling (self);
+
   priv->end_scroll = gtk_button_new_from_icon_name ("pan-end-symbolic", GTK_ICON_SIZE_MENU);
   gtk_button_set_relief (GTK_BUTTON (priv->end_scroll), GTK_RELIEF_NONE);
   gtk_widget_show (priv->end_scroll);
@@ -335,8 +383,13 @@ gtk_tab_strip_init (GtkTabStrip *self)
                     G_CALLBACK (scroll_button_activate), self);
 
   adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
-  g_signal_connect (adj, "changed", G_CALLBACK (adjustment_changed), self);
-  g_signal_connect (adj, "value-changed", G_CALLBACK (adjustment_changed), self);
+  g_signal_connect_swapped (adj, "changed", G_CALLBACK (adjustment_changed), self);
+  g_signal_connect_swapped (adj, "value-changed", G_CALLBACK (adjustment_changed), self);
+
+  adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
+  g_signal_connect_swapped (adj, "changed", G_CALLBACK (adjustment_changed), self);
+  g_signal_connect_swapped (adj, "value-changed", G_CALLBACK (adjustment_changed), self);
+
 
   priv->tabs = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
   gtk_widget_show (priv->tabs);
@@ -460,6 +513,8 @@ gtk_tab_strip_stack_add (GtkTabStrip *self,
 
   g_signal_emit (self, signals[CREATE_TAB], 0, widget, &tab);
 
+  gtk_tab_set_edge (tab, priv->edge);
+
   g_object_set_data (G_OBJECT (widget), "GTK_TAB", tab);
 
   g_signal_connect (tab, "activate",
@@ -581,6 +636,86 @@ gtk_tab_strip_set_stack (GtkTabStrip *self,
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STACK]);
 }
 
+GtkPositionType
+gtk_tab_strip_get_edge (GtkTabStrip *self)
+{
+  GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
+
+  g_return_val_if_fail (GTK_IS_TAB_STRIP (self), GTK_POS_TOP);
+
+  return priv->edge;
+}
+
+static void
+update_edge (GtkWidget *widget,
+             gpointer   data)
+{
+  GtkTabStrip *self = data;
+  GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
+
+  if (GTK_IS_TAB (widget))
+    gtk_tab_set_edge (GTK_TAB (widget), priv->edge);
+}
+
+void
+gtk_tab_strip_set_edge (GtkTabStrip     *self,
+                        GtkPositionType  edge)
+{
+  GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
+  GtkStyleContext *context;
+  GtkOrientation orientation;
+  GtkWidget *image;
+  const char *classes[] = {
+    "left",
+    "right",
+    "top",
+    "bottom"
+  };
+  GtkOrientation orientations[] = {
+    GTK_ORIENTATION_VERTICAL,
+    GTK_ORIENTATION_VERTICAL,
+    GTK_ORIENTATION_HORIZONTAL,
+    GTK_ORIENTATION_HORIZONTAL
+  };
+  const char *start_icon[] = {
+    "pan-start-symbolic",
+    "pan-up-symbolic"
+  };
+  const char *end_icon[] = {
+    "pan-end-symbolic",
+    "pan-down-symbolic"
+  };
+
+  g_return_if_fail (GTK_IS_TAB_STRIP (self));
+
+  if (priv->edge == edge)
+    return;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (self));
+  gtk_style_context_remove_class (context, classes[priv->edge]);
+
+  priv->edge = edge;
+
+  gtk_style_context_add_class (context, classes[priv->edge]);
+
+  orientation = orientations[priv->edge];
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (self), orientation);
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->tabs), orientation);
+
+  image = gtk_button_get_image (GTK_BUTTON (priv->start_scroll));
+  gtk_image_set_from_icon_name (GTK_IMAGE (image), start_icon[orientation], GTK_ICON_SIZE_MENU);
+
+  image = gtk_button_get_image (GTK_BUTTON (priv->end_scroll));
+  gtk_image_set_from_icon_name (GTK_IMAGE (image), end_icon[orientation], GTK_ICON_SIZE_MENU);
+
+  update_scrolling (self);
+  adjustment_changed (self);
+
+  gtk_container_foreach (GTK_CONTAINER (priv->tabs), update_edge, self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDGE]);
+}
+
 void
 gtk_tab_strip_set_closable (GtkTabStrip *self,
                             gboolean     closable)
@@ -620,9 +755,7 @@ gtk_tab_strip_set_scrollable (GtkTabStrip *self,
 
   priv->scrollable = scrollable;
 
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
-                                  scrollable ? GTK_POLICY_EXTERNAL : GTK_POLICY_NEVER,
-                                  GTK_POLICY_NEVER);
+  update_scrolling (self);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SCROLLABLE]);
 }
diff --git a/gtk/gtktabstrip.h b/gtk/gtktabstrip.h
index ccb4f7c..2a38dd2 100644
--- a/gtk/gtktabstrip.h
+++ b/gtk/gtktabstrip.h
@@ -72,6 +72,11 @@ GDK_AVAILABLE_IN_3_22
 void             gtk_tab_strip_set_stack       (GtkTabStrip     *self,
                                                 GtkStack        *stack);
 GDK_AVAILABLE_IN_3_22
+GtkPositionType  gtk_tab_strip_get_edge        (GtkTabStrip     *self);
+GDK_AVAILABLE_IN_3_22
+void             gtk_tab_strip_set_edge        (GtkTabStrip     *self,
+                                                GtkPositionType  edge);
+GDK_AVAILABLE_IN_3_22
 gboolean         gtk_tab_strip_get_closable    (GtkTabStrip     *self);
 GDK_AVAILABLE_IN_3_22
 void             gtk_tab_strip_set_closable    (GtkTabStrip     *self,


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