Add a tab strip widget

This adds GtkTabStrip and GtkTab, which will eventually allow
creating typical notebook scenarios with GtkStack.

Inspired by the PnlTabStrip prototype that Christian Hergert
wrote for gnome-builder.
This commit is contained in:
Matthias Clasen
2016-05-21 12:50:11 -04:00
parent 21804b09b4
commit e6e418f6e4
6 changed files with 1211 additions and 0 deletions

View File

@@ -302,6 +302,8 @@ gtk_public_h_sources = \
gtkstylecontext.h \
gtkstyleprovider.h \
gtkswitch.h \
gtktab.h \
gtktabstrip.h \
gtktestutils.h \
gtktextattributes.h \
gtktextbuffer.h \
@@ -900,6 +902,8 @@ gtk_base_c_sources = \
gtkstyleprovider.c \
gtkstyleproviderprivate.c \
gtkswitch.c \
gtktab.c \
gtktabstrip.c \
gtktestutils.c \
gtktextattributes.c \
gtktextbtree.c \

View File

@@ -203,6 +203,8 @@
#include <gtk/gtkstylecontext.h>
#include <gtk/gtkstyleprovider.h>
#include <gtk/gtkswitch.h>
#include <gtk/gtktab.h>
#include <gtk/gtktabstrip.h>
#include <gtk/gtktextattributes.h>
#include <gtk/gtktextbuffer.h>
#include <gtk/gtktextbufferrichtext.h>

519
gtk/gtktab.c Normal file
View File

@@ -0,0 +1,519 @@
/* gtktab.c
*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtktab.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtkenums.h"
#include "gtktypebuiltins.h"
#include "gtkboxgadgetprivate.h"
#include "gtkwidgetprivate.h"
typedef struct _GtkTabPrivate GtkTabPrivate;
struct _GtkTabPrivate
{
gchar *title;
GtkWidget *widget;
GtkWidget *child;
GtkCssGadget *gadget;
GdkWindow *event_window;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkTab, gtk_tab, GTK_TYPE_CONTAINER)
enum {
PROP_0,
PROP_TITLE,
PROP_WIDGET,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
enum {
ACTIVATE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
static void
gtk_tab_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTab *self = GTK_TAB (object);
switch (prop_id)
{
case PROP_TITLE:
g_value_set_string (value, gtk_tab_get_title (self));
break;
case PROP_WIDGET:
g_value_set_object (value, gtk_tab_get_widget (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_tab_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTab *self = GTK_TAB (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_tab_set_title (self, g_value_get_string (value));
break;
case PROP_WIDGET:
gtk_tab_set_widget (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_tab_finalize (GObject *object)
{
GtkTab *self = GTK_TAB (object);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
g_free (priv->title);
g_clear_object (&priv->gadget);
G_OBJECT_CLASS (gtk_tab_parent_class)->finalize (object);
}
static void
gtk_tab_destroy (GtkWidget *widget)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
if (priv->widget)
{
g_object_remove_weak_pointer (G_OBJECT (priv->widget), (gpointer *)&priv->widget);
priv->widget = NULL;
}
GTK_WIDGET_CLASS (gtk_tab_parent_class)->destroy (widget);
}
static void
gtk_tab_realize (GtkWidget *widget)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
GtkAllocation allocation;
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
window = gtk_widget_get_parent_window (widget);
gtk_widget_set_window (widget, window);
g_object_ref (window);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y;
priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_register_window (widget, priv->event_window);
}
static void
gtk_tab_unrealize (GtkWidget *widget)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_widget_unregister_window (widget, priv->event_window);
gdk_window_destroy (priv->event_window);
priv->event_window = NULL;
GTK_WIDGET_CLASS (gtk_tab_parent_class)->unrealize (widget);
}
static void
gtk_tab_map (GtkWidget *widget)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
GTK_WIDGET_CLASS (gtk_tab_parent_class)->map (widget);
gdk_window_show_unraised (priv->event_window);
}
static void
gtk_tab_unmap (GtkWidget *widget)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gdk_window_hide (priv->event_window);
GTK_WIDGET_CLASS (gtk_tab_parent_class)->unmap (widget);
}
static gboolean
gtk_tab_enter (GtkWidget *widget,
GdkEventCrossing *event)
{
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
return TRUE;
}
static gboolean
gtk_tab_leave (GtkWidget *widget,
GdkEventCrossing *event)
{
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
return TRUE;
}
static gboolean
gtk_tab_button_press (GtkWidget *widget,
GdkEventButton *event)
{
if (event->button != GDK_BUTTON_PRIMARY)
return FALSE;
g_signal_emit (widget, signals[ACTIVATE], 0);
return TRUE;
}
static void
gtk_tab_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_css_gadget_get_preferred_size (priv->gadget,
GTK_ORIENTATION_HORIZONTAL,
-1,
minimum, natural,
NULL, NULL);
}
static void
gtk_tab_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_css_gadget_get_preferred_size (priv->gadget,
GTK_ORIENTATION_VERTICAL,
-1,
minimum, natural,
NULL, NULL);
}
static void
gtk_tab_get_preferred_width_for_height (GtkWidget *widget,
gint height,
gint *minimum,
gint *natural)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_css_gadget_get_preferred_size (priv->gadget,
GTK_ORIENTATION_HORIZONTAL,
height,
minimum, natural,
NULL, NULL);
}
static void
gtk_tab_get_preferred_height_for_width (GtkWidget *widget,
gint width,
gint *minimum,
gint *natural)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_css_gadget_get_preferred_size (priv->gadget,
GTK_ORIENTATION_VERTICAL,
width,
minimum, natural,
NULL, NULL);
}
static void
gtk_tab_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
GtkAllocation clip;
gtk_widget_set_allocation (widget, allocation);
gtk_css_gadget_allocate (priv->gadget,
allocation,
gtk_widget_get_allocated_baseline (widget),
&clip);
gtk_widget_set_clip (widget, &clip);
if (gtk_widget_get_realized (widget))
{
GtkAllocation border_allocation;
gtk_css_gadget_get_border_allocation (priv->gadget, &border_allocation, NULL);
gdk_window_move_resize (priv->event_window,
border_allocation.x, border_allocation.y,
border_allocation.width, border_allocation.height);
if (gtk_widget_get_mapped (widget))
gdk_window_show_unraised (priv->event_window);
}
}
static gboolean
gtk_tab_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkTab *self = GTK_TAB (widget);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
gtk_css_gadget_draw (priv->gadget, cr);
return FALSE;
}
static void
gtk_tab_add (GtkContainer *container,
GtkWidget *child)
{
GtkTab *self = GTK_TAB (container);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
if (priv->child)
{
g_warning ("GtkTab cannot have more than one child");
return;
}
priv->child = child;
gtk_widget_set_parent (child, GTK_WIDGET (container));
gtk_box_gadget_insert_widget (GTK_BOX_GADGET (priv->gadget), 0, child);
gtk_box_gadget_set_gadget_expand (GTK_BOX_GADGET (priv->gadget), G_OBJECT (child), TRUE);
}
static void
gtk_tab_remove (GtkContainer *container,
GtkWidget *child)
{
GtkTab *self = GTK_TAB (container);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
if (priv->child == child)
{
gtk_box_gadget_remove_widget (GTK_BOX_GADGET (priv->gadget), child);
gtk_widget_unparent (child);
priv->child = NULL;
}
}
static void
gtk_tab_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer data)
{
GtkTab *self = GTK_TAB (container);
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
if (priv->child)
(*callback) (priv->child, data);
}
static void
gtk_tab_class_init (GtkTabClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
object_class->get_property = gtk_tab_get_property;
object_class->set_property = gtk_tab_set_property;
object_class->finalize = gtk_tab_finalize;
widget_class->destroy = gtk_tab_destroy;
widget_class->realize = gtk_tab_realize;
widget_class->unrealize = gtk_tab_unrealize;
widget_class->map = gtk_tab_map;
widget_class->unmap = gtk_tab_unmap;
widget_class->enter_notify_event = gtk_tab_enter;
widget_class->leave_notify_event = gtk_tab_leave;
widget_class->button_press_event = gtk_tab_button_press;
widget_class->get_preferred_width = gtk_tab_get_preferred_width;
widget_class->get_preferred_height = gtk_tab_get_preferred_height;
widget_class->get_preferred_width_for_height = gtk_tab_get_preferred_width_for_height;
widget_class->get_preferred_height_for_width = gtk_tab_get_preferred_height_for_width;
widget_class->size_allocate = gtk_tab_size_allocate;
widget_class->draw = gtk_tab_draw;
container_class->add = gtk_tab_add;
container_class->remove = gtk_tab_remove;
container_class->forall = gtk_tab_forall;
gtk_widget_class_set_css_name (widget_class, "tab");
properties[PROP_TITLE] =
g_param_spec_string ("title", P_("Title"), P_("Title"),
NULL,
GTK_PARAM_READWRITE);
properties[PROP_WIDGET] =
g_param_spec_object ("widget", P_("Widget"), P_("The widget the tab represents"),
GTK_TYPE_WIDGET,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, N_PROPS, properties);
signals[ACTIVATE] =
g_signal_new (I_("activate"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTabClass, activate),
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
widget_class->activate_signal = signals[ACTIVATE];
}
static void
gtk_tab_init (GtkTab *self)
{
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
GtkCssNode *widget_node;
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
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);
}
const gchar *
gtk_tab_get_title (GtkTab *self)
{
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
g_return_val_if_fail (GTK_IS_TAB (self), NULL);
return priv->title;
}
void
gtk_tab_set_title (GtkTab *self,
const gchar *title)
{
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
g_return_if_fail (GTK_IS_TAB (self));
g_free (priv->title);
priv->title = g_strdup (title);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
}
GtkWidget *
gtk_tab_get_widget (GtkTab *self)
{
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
g_return_val_if_fail (GTK_IS_TAB (self), NULL);
return priv->widget;
}
void
gtk_tab_set_widget (GtkTab *self,
GtkWidget *widget)
{
GtkTabPrivate *priv = gtk_tab_get_instance_private (self);
g_return_if_fail (GTK_IS_TAB (self));
if (priv->widget == widget)
return;
if (priv->widget)
g_object_remove_weak_pointer (G_OBJECT (priv->widget), (gpointer *)&priv->widget);
priv->widget = widget;
if (widget)
g_object_add_weak_pointer (G_OBJECT (priv->widget), (gpointer *)&priv->widget);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDGET]);
}
void
gtk_tab_set_child (GtkTab *self,
GtkWidget *child)
{
g_return_if_fail (GTK_IS_TAB (self));
g_return_if_fail (GTK_IS_WIDGET (child));
gtk_tab_add (GTK_CONTAINER (self), child);
}

75
gtk/gtktab.h Normal file
View File

@@ -0,0 +1,75 @@
/* gtktab.h
*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_TAB_H__
#define __GTK_TAB_H__
#include <gtk/gtkcontainer.h>
G_BEGIN_DECLS
#define GTK_TYPE_TAB (gtk_tab_get_type ())
#define GTK_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TAB, GtkTab))
#define GTK_IS_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TAB))
#define GTK_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TAB, GtkTabClass))
#define GTK_IS_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TAB))
#define GTK_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TAB, GtkTabClass))
typedef struct _GtkTab GtkTab;
typedef struct _GtkTabClass GtkTabClass;
struct _GtkTab
{
GtkContainer parent;
};
struct _GtkTabClass
{
GtkContainerClass parent_class;
void (* activate) (GtkTab *tab);
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
void (*_gtk_reserved2) (void);
void (*_gtk_reserved3) (void);
void (*_gtk_reserved4) (void);
void (*_gtk_reserved5) (void);
void (*_gtk_reserved6) (void);
};
GDK_AVAILABLE_IN_3_22
GType gtk_tab_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
const gchar *gtk_tab_get_title (GtkTab *self);
GDK_AVAILABLE_IN_3_22
void gtk_tab_set_title (GtkTab *self,
const gchar *title);
GDK_AVAILABLE_IN_3_22
GtkWidget *gtk_tab_get_widget (GtkTab *self);
GDK_AVAILABLE_IN_3_22
void gtk_tab_set_widget (GtkTab *self,
GtkWidget *widget);
GDK_AVAILABLE_IN_3_22
void gtk_tab_set_child (GtkTab *self,
GtkWidget *child);
G_END_DECLS
#endif /* __GTK_TAB_H__ */

546
gtk/gtktabstrip.c Normal file
View File

@@ -0,0 +1,546 @@
/* gtktabstrip.c
*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtktabstrip.h"
#include "gtktab.h"
#include "gtksimpletab.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtkorientable.h"
typedef struct
{
GtkStack *stack;
gboolean in_child_changed;
GdkWindow *event_window;
} GtkTabStripPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (GtkTabStrip, gtk_tab_strip, GTK_TYPE_BOX)
enum {
PROP_0,
PROP_STACK,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void
gtk_tab_strip_add (GtkContainer *container,
GtkWidget *widget)
{
GTK_CONTAINER_CLASS (gtk_tab_strip_parent_class)->add (container, widget);
}
static void
gtk_tab_strip_destroy (GtkWidget *widget)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
gtk_tab_strip_set_stack (self, NULL);
g_clear_object (&priv->stack);
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->destroy (widget);
}
static void
gtk_tab_strip_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTabStrip *self = GTK_TAB_STRIP (object);
switch (prop_id)
{
case PROP_STACK:
g_value_set_object (value, gtk_tab_strip_get_stack (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_tab_strip_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTabStrip *self = GTK_TAB_STRIP (object);
switch (prop_id)
{
case PROP_STACK:
gtk_tab_strip_set_stack (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static gboolean
get_widget_coordinates (GtkWidget *widget,
GdkEvent *event,
gdouble *x,
gdouble *y)
{
GdkWindow *window = ((GdkEventAny *)event)->window;
gdouble tx, ty;
if (!gdk_event_get_coords (event, &tx, &ty))
return FALSE;
while (window && window != gtk_widget_get_window (widget))
{
gint window_x, window_y;
gdk_window_get_position (window, &window_x, &window_y);
tx += window_x;
ty += window_y;
window = gdk_window_get_parent (window);
}
if (window)
{
*x = tx;
*y = ty;
return TRUE;
}
else
return FALSE;
}
static GtkTab *
get_tab_at_pos (GtkTabStrip *self,
gdouble x,
gdouble y)
{
GtkAllocation allocation;
GList *children, *l;
GtkTab *tab;
children = gtk_container_get_children (GTK_CONTAINER (self));
tab = NULL;
for (l = children; l; l = l->next)
{
gtk_widget_get_allocation (GTK_WIDGET (l->data), &allocation);
if ((x >= allocation.x) &&
(y >= allocation.y) &&
(x <= (allocation.x + allocation.width)) &&
(y <= (allocation.y + allocation.height)))
{
tab = l->data;
break;
}
}
g_list_free (children);
return tab;
}
static gboolean
gtk_tab_strip_button_press (GtkWidget *widget,
GdkEventButton *event)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GtkTab *tab;
GtkWidget *child;
gdouble x, y;
if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
return FALSE;
if (event->button != GDK_BUTTON_PRIMARY)
return FALSE;
tab = get_tab_at_pos (self, x, y);
if (tab == NULL)
return FALSE;
child = gtk_tab_get_widget (tab);
if (child == NULL)
return FALSE;
gtk_stack_set_visible_child (priv->stack, child);
return TRUE;
}
static void
gtk_tab_strip_map (GtkWidget *widget)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->map (widget);
gdk_window_show_unraised (priv->event_window);
}
static void
gtk_tab_strip_unmap (GtkWidget *widget)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
gdk_window_hide (priv->event_window);
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->unmap (widget);
}
static void
gtk_tab_strip_realize (GtkWidget *widget)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
GtkAllocation allocation;
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
window = gtk_widget_get_parent_window (widget);
gtk_widget_set_window (widget, window);
g_object_ref (window);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y;
priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_register_window (widget, priv->event_window);
}
static void
gtk_tab_strip_unrealize (GtkWidget *widget)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
gtk_widget_unregister_window (widget, priv->event_window);
gdk_window_destroy (priv->event_window);
priv->event_window = NULL;
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->unrealize (widget);
}
static void
gtk_tab_strip_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkTabStrip *self = GTK_TAB_STRIP (widget);
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->size_allocate (widget, allocation);
if (gtk_widget_get_realized (widget))
{
gdk_window_move_resize (priv->event_window,
allocation->x, allocation->y,
allocation->width, allocation->height);
if (gtk_widget_get_mapped (widget))
gdk_window_show_unraised (priv->event_window);
}
}
static void
gtk_tab_strip_class_init (GtkTabStripClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
object_class->get_property = gtk_tab_strip_get_property;
object_class->set_property = gtk_tab_strip_set_property;
widget_class->destroy = gtk_tab_strip_destroy;
widget_class->map = gtk_tab_strip_map;
widget_class->unmap = gtk_tab_strip_unmap;
widget_class->realize = gtk_tab_strip_realize;
widget_class->unrealize = gtk_tab_strip_unrealize;
widget_class->size_allocate = gtk_tab_strip_size_allocate;
widget_class->button_press_event = gtk_tab_strip_button_press;
container_class->add = gtk_tab_strip_add;
properties[PROP_STACK] =
g_param_spec_object ("stack", P_("Stack"), P_("The stack of items to manage"),
GTK_TYPE_STACK,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, N_PROPS, properties);
gtk_widget_class_set_css_name (widget_class, "tabs");
}
static void
gtk_tab_strip_init (GtkTabStrip *self)
{
}
static void
gtk_tab_strip_child_position_changed (GtkTabStrip *self,
GParamSpec *pspec,
GtkWidget *child)
{
GtkWidget *parent;
GtkTab *tab;
guint position;
tab = g_object_get_data (G_OBJECT (child), "GTK_TAB");
if (!tab || !GTK_IS_TAB (tab))
return;
parent = gtk_widget_get_parent (child);
gtk_container_child_get (GTK_CONTAINER (parent), child,
"position", &position,
NULL);
gtk_container_child_set (GTK_CONTAINER (self), GTK_WIDGET (tab),
"position", position,
NULL);
}
static void
gtk_tab_strip_child_title_changed (GtkTabStrip *self,
GParamSpec *pspec,
GtkWidget *child)
{
g_autofree gchar *title = NULL;
GtkWidget *parent;
GtkTab *tab;
tab = g_object_get_data (G_OBJECT (child), "GTK_TAB");
if (!GTK_IS_TAB (tab))
return;
parent = gtk_widget_get_parent (child);
gtk_container_child_get (GTK_CONTAINER (parent), child,
"title", &title,
NULL);
gtk_tab_set_title (tab, title);
}
static void
update_visible_child (GtkWidget *tab,
gpointer user_data)
{
GtkWidget *visible_child = user_data;
if (GTK_IS_TAB (tab))
{
if (gtk_tab_get_widget (GTK_TAB (tab)) == visible_child)
gtk_widget_set_state_flags (tab, GTK_STATE_FLAG_CHECKED, FALSE);
else
gtk_widget_unset_state_flags (tab, GTK_STATE_FLAG_CHECKED);
}
}
static void
gtk_tab_strip_stack_notify_visible_child (GtkTabStrip *self,
GParamSpec *pspec,
GtkStack *stack)
{
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GtkWidget *visible_child;
visible_child = gtk_stack_get_visible_child (stack);
priv->in_child_changed = TRUE;
gtk_container_foreach (GTK_CONTAINER (self), update_visible_child, visible_child);
priv->in_child_changed = FALSE;
}
static void
tab_activated (GtkTab *tab,
GtkTabStrip *self)
{
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
GtkWidget *widget;
widget = gtk_tab_get_widget (tab);
if (widget)
gtk_stack_set_visible_child (priv->stack, widget);
}
static void
gtk_tab_strip_stack_add (GtkTabStrip *self,
GtkWidget *widget,
GtkStack *stack)
{
GtkTab *tab;
gint position = 0;
gtk_container_child_get (GTK_CONTAINER (stack), widget,
"position", &position,
NULL);
tab = g_object_new (GTK_TYPE_SIMPLE_TAB,
"widget", widget,
NULL);
g_object_set_data (G_OBJECT (widget), "GTK_TAB", tab);
g_signal_connect (tab, "activate",
G_CALLBACK (tab_activated), self);
g_signal_connect_object (widget, "child-notify::position",
G_CALLBACK (gtk_tab_strip_child_position_changed), self,
G_CONNECT_SWAPPED);
g_signal_connect_object (widget, "child-notify::title",
G_CALLBACK (gtk_tab_strip_child_title_changed), self,
G_CONNECT_SWAPPED);
gtk_box_pack_start (GTK_BOX (self), GTK_WIDGET (tab), TRUE, TRUE, 0);
g_object_bind_property (widget, "visible", tab, "visible", G_BINDING_SYNC_CREATE);
gtk_tab_strip_child_title_changed (self, NULL, widget);
gtk_tab_strip_stack_notify_visible_child (self, NULL, stack);
}
static void
gtk_tab_strip_stack_remove (GtkTabStrip *self,
GtkWidget *widget,
GtkStack *stack)
{
GtkTab *tab;
tab = g_object_get_data (G_OBJECT (widget), "GTK_TAB");
if (GTK_IS_TAB (tab))
gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (tab));
}
GtkWidget *
gtk_tab_strip_new (void)
{
return g_object_new (GTK_TYPE_TAB_STRIP, NULL);
}
GtkStack *
gtk_tab_strip_get_stack (GtkTabStrip *self)
{
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
g_return_val_if_fail (GTK_IS_TAB_STRIP (self), NULL);
return priv->stack;
}
static void
gtk_tab_strip_cold_plug (GtkWidget *widget,
gpointer user_data)
{
GtkTabStrip *self = user_data;
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
gtk_tab_strip_stack_add (self, widget, priv->stack);
}
void
gtk_tab_strip_set_stack (GtkTabStrip *self,
GtkStack *stack)
{
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
g_return_if_fail (GTK_IS_TAB_STRIP (self));
g_return_if_fail (!stack || GTK_IS_STACK (stack));
if (priv->stack == stack)
return;
if (priv->stack != NULL)
{
g_signal_handlers_disconnect_by_func (priv->stack,
G_CALLBACK (gtk_tab_strip_stack_notify_visible_child),
self);
g_signal_handlers_disconnect_by_func (priv->stack,
G_CALLBACK (gtk_tab_strip_stack_add),
self);
g_signal_handlers_disconnect_by_func (priv->stack,
G_CALLBACK (gtk_tab_strip_stack_remove),
self);
gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback)gtk_widget_destroy, NULL);
g_clear_object (&priv->stack);
}
if (stack != NULL)
{
priv->stack = g_object_ref (stack);
g_signal_connect_object (priv->stack,
"notify::visible-child",
G_CALLBACK (gtk_tab_strip_stack_notify_visible_child),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->stack,
"add",
G_CALLBACK (gtk_tab_strip_stack_add),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->stack,
"remove",
G_CALLBACK (gtk_tab_strip_stack_remove),
self,
G_CONNECT_SWAPPED);
gtk_container_foreach (GTK_CONTAINER (priv->stack),
gtk_tab_strip_cold_plug,
self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STACK]);
}

65
gtk/gtktabstrip.h Normal file
View File

@@ -0,0 +1,65 @@
/* gtktabstrip.h
*
* Copyright (C) 2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__GTK_H_INSIDE__) && !defined(GTK_COMPILATION)
# error "Only <gtk/gtk.h> can be included directly."
#endif
#ifndef __GTK_TAB_STRIP_H__
#define __GTK_TAB_STRIP_H__
#include <gtk/gtkbox.h>
#include <gtk/gtkstack.h>
#include <gtk/gtktypebuiltins.h>
G_BEGIN_DECLS
#define GTK_TYPE_TAB_STRIP (gtk_tab_strip_get_type ())
#define GTK_TAB_STRIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TAB_STRIP, GtkTabStrip))
#define GTK_IS_TAB_STRIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TAB_STRIP))
#define GTK_TAB_STRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TAB_STRIP, GtkTabStripClass))
#define GTK_IS_TAB_STRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TAB_STRIP))
#define GTK_TAB_STRIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TAB_STRIP, GtkTabStripClass))
typedef struct _GtkTabStrip GtkTabStrip;
typedef struct _GtkTabStripClass GtkTabStripClass;
struct _GtkTabStrip
{
GtkBox parent;
};
struct _GtkTabStripClass
{
GtkBoxClass parent_class;
};
GDK_AVAILABLE_IN_3_22
GType gtk_tab_strip_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
GtkWidget *gtk_tab_strip_new (void);
GDK_AVAILABLE_IN_3_22
GtkStack *gtk_tab_strip_get_stack (GtkTabStrip *self);
GDK_AVAILABLE_IN_3_22
void gtk_tab_strip_set_stack (GtkTabStrip *self,
GtkStack *stack);
G_END_DECLS
#endif /* __GTK_TAB_STRIP_H__ */