Commit 1525f4b2 authored by Matthias Clasen's avatar Matthias Clasen

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.
parent e13f564f
......@@ -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");
}
......@@ -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,
......
......@@ -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]);
}
......
......@@ -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,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment