Compare commits
40 Commits
gridview-s
...
wip/matthi
Author | SHA1 | Date | |
---|---|---|---|
|
3166b30303 | ||
|
e7b0a47856 | ||
|
73e7219b53 | ||
|
6761f70a3a | ||
|
2e66f4201a | ||
|
e82f0f517b | ||
|
123f39eb55 | ||
|
9552ced2d8 | ||
|
cdc42cec2f | ||
|
b21d150144 | ||
|
c9ec72be7a | ||
|
4d880268fc | ||
|
df07faa81b | ||
|
ce2c6eff1e | ||
|
f86f439ed5 | ||
|
7dd60e3986 | ||
|
98a80b0c8f | ||
|
3afd1b14f0 | ||
|
397332702e | ||
|
c05ce8cf9f | ||
|
2c944b7928 | ||
|
9f1fc47175 | ||
|
4e0c88a3d8 | ||
|
0a06bb66db | ||
|
b875aaba99 | ||
|
39d863caae | ||
|
3927f44a0d | ||
|
68b1814e83 | ||
|
d6f5af4df9 | ||
|
bf97ddeaaa | ||
|
1525f4b2e7 | ||
|
e13f564fc2 | ||
|
6558ab4bb3 | ||
|
76da4b4860 | ||
|
cb65a174a5 | ||
|
f01d8b105a | ||
|
2b0c34cbf8 | ||
|
941f1ad1a6 | ||
|
e6e418f6e4 | ||
|
21804b09b4 |
@@ -302,6 +302,7 @@ gtk_public_h_sources = \
|
||||
gtkstylecontext.h \
|
||||
gtkstyleprovider.h \
|
||||
gtkswitch.h \
|
||||
gtktabstrip.h \
|
||||
gtktestutils.h \
|
||||
gtktextattributes.h \
|
||||
gtktextbuffer.h \
|
||||
@@ -559,6 +560,9 @@ gtk_private_h_sources = \
|
||||
gtkstylecontextprivate.h \
|
||||
gtkstylepropertyprivate.h \
|
||||
gtkstyleproviderprivate.h \
|
||||
gtktab.h \
|
||||
gtksimpletab.h \
|
||||
gtkclosabletab.h \
|
||||
gtktextattributesprivate.h \
|
||||
gtktextbtree.h \
|
||||
gtktextbufferprivate.h \
|
||||
@@ -900,6 +904,11 @@ gtk_base_c_sources = \
|
||||
gtkstyleprovider.c \
|
||||
gtkstyleproviderprivate.c \
|
||||
gtkswitch.c \
|
||||
gtktab.c \
|
||||
gtksimpletab.c \
|
||||
gtkclosabletab.c \
|
||||
gtktabs.c \
|
||||
gtktabstrip.c \
|
||||
gtktestutils.c \
|
||||
gtktextattributes.c \
|
||||
gtktextbtree.c \
|
||||
|
@@ -203,6 +203,7 @@
|
||||
#include <gtk/gtkstylecontext.h>
|
||||
#include <gtk/gtkstyleprovider.h>
|
||||
#include <gtk/gtkswitch.h>
|
||||
#include <gtk/gtktabstrip.h>
|
||||
#include <gtk/gtktextattributes.h>
|
||||
#include <gtk/gtktextbuffer.h>
|
||||
#include <gtk/gtktextbufferrichtext.h>
|
||||
|
85
gtk/gtkclosabletab.c
Normal file
85
gtk/gtkclosabletab.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/* gtkclosabletab.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 "gtkclosabletab.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkenums.h"
|
||||
#include "gtklabel.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkbutton.h"
|
||||
|
||||
struct _GtkClosableTab
|
||||
{
|
||||
GtkTab parent;
|
||||
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
GtkWidget *button;
|
||||
};
|
||||
|
||||
typedef struct _GtkClosableTabClass GtkClosableTabClass;
|
||||
|
||||
struct _GtkClosableTabClass
|
||||
{
|
||||
GtkTabClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkClosableTab, gtk_closable_tab, GTK_TYPE_TAB)
|
||||
|
||||
static void
|
||||
close_tab (GtkClosableTab *tab)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GtkWidget *stack;
|
||||
|
||||
widget = gtk_tab_get_widget (GTK_TAB (tab));
|
||||
stack = gtk_widget_get_parent (widget);
|
||||
gtk_container_remove (GTK_CONTAINER (stack), widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_closable_tab_init (GtkClosableTab *self)
|
||||
{
|
||||
self->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
|
||||
gtk_widget_show (self->box);
|
||||
gtk_tab_set_child (GTK_TAB (self), self->box);
|
||||
|
||||
self->label = gtk_label_new ("");
|
||||
gtk_widget_set_halign (self->label, GTK_ALIGN_START);
|
||||
gtk_label_set_width_chars (GTK_LABEL (self->label), 10);
|
||||
gtk_label_set_max_width_chars (GTK_LABEL (self->label), 25);
|
||||
gtk_label_set_ellipsize (GTK_LABEL (self->label), PANGO_ELLIPSIZE_END);
|
||||
gtk_widget_show (self->label);
|
||||
gtk_box_pack_start (GTK_BOX (self->box), self->label, TRUE, TRUE, 0);
|
||||
g_object_bind_property (self, "title", self->label, "label", G_BINDING_DEFAULT);
|
||||
|
||||
self->button = gtk_button_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
|
||||
gtk_widget_show (self->button);
|
||||
gtk_button_set_relief (GTK_BUTTON (self->button), GTK_RELIEF_NONE);
|
||||
gtk_box_pack_end (GTK_BOX (self->box), self->button, FALSE, FALSE, 0);
|
||||
|
||||
g_signal_connect_swapped (self->button, "clicked", G_CALLBACK (close_tab), self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_closable_tab_class_init (GtkClosableTabClass *klass)
|
||||
{
|
||||
}
|
36
gtk/gtkclosabletab.h
Normal file
36
gtk/gtkclosabletab.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* gtkclosabletab.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_CLOSABLE_TAB_H__
|
||||
#define __GTK_CLOSABLE_TAB_H__
|
||||
|
||||
#include <gtk/gtktab.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CLOSABLE_TAB (gtk_closable_tab_get_type ())
|
||||
#define GTK_CLOSABLE_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLOSABLE_TAB, GtkClosableTab))
|
||||
#define GTK_IS_CLOSABLE_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLOSABLE_TAB))
|
||||
|
||||
typedef struct _GtkClosableTab GtkClosableTab;
|
||||
|
||||
GType gtk_closable_tab_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CLOSABLE_TAB_H__ */
|
@@ -644,22 +644,6 @@ add_reorder_bindings (GtkBindingSet *binding_set,
|
||||
G_TYPE_BOOLEAN, move_to_last);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy)
|
||||
{
|
||||
gboolean continue_emission;
|
||||
GObject *object;
|
||||
|
||||
object = g_value_get_object (handler_return);
|
||||
g_value_set_object (return_accu, object);
|
||||
continue_emission = !object;
|
||||
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_notebook_compute_expand (GtkWidget *widget,
|
||||
gboolean *hexpand_p,
|
||||
|
@@ -158,6 +158,22 @@ _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy)
|
||||
{
|
||||
gboolean continue_emission;
|
||||
GObject *object;
|
||||
|
||||
object = g_value_get_object (handler_return);
|
||||
g_value_set_object (return_accu, object);
|
||||
continue_emission = !object;
|
||||
|
||||
return continue_emission;
|
||||
}
|
||||
|
||||
GdkModifierType
|
||||
_gtk_replace_virtual_modifiers (GdkKeymap *keymap,
|
||||
GdkModifierType modifiers)
|
||||
|
@@ -73,6 +73,11 @@ gboolean _gtk_single_string_accumulator (GSignalInvocationHint *ihint,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy);
|
||||
|
||||
gboolean gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer dummy);
|
||||
|
||||
GdkModifierType _gtk_replace_virtual_modifiers (GdkKeymap *keymap,
|
||||
GdkModifierType modifiers);
|
||||
GdkModifierType _gtk_get_primary_accel_mod (void);
|
||||
|
62
gtk/gtksimpletab.c
Normal file
62
gtk/gtksimpletab.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/* gtksimpletab.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 "gtksimpletab.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkenums.h"
|
||||
#include "gtklabel.h"
|
||||
|
||||
struct _GtkSimpleTab
|
||||
{
|
||||
GtkTab parent;
|
||||
|
||||
GtkWidget *label;
|
||||
};
|
||||
|
||||
typedef struct _GtkSimpleTabClass GtkSimpleTabClass;
|
||||
|
||||
struct _GtkSimpleTabClass
|
||||
{
|
||||
GtkTabClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkSimpleTab, gtk_simple_tab, GTK_TYPE_TAB)
|
||||
|
||||
static void
|
||||
gtk_simple_tab_class_init (GtkSimpleTabClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_simple_tab_init (GtkSimpleTab *self)
|
||||
{
|
||||
self->label = gtk_label_new ("");
|
||||
gtk_widget_set_halign (self->label, GTK_ALIGN_START);
|
||||
gtk_label_set_width_chars (GTK_LABEL (self->label), 10);
|
||||
gtk_label_set_max_width_chars (GTK_LABEL (self->label), 25);
|
||||
gtk_label_set_ellipsize (GTK_LABEL (self->label), PANGO_ELLIPSIZE_END);
|
||||
gtk_widget_show (self->label);
|
||||
gtk_widget_set_halign (self->label, GTK_ALIGN_CENTER);
|
||||
|
||||
gtk_tab_set_child (GTK_TAB (self), self->label);
|
||||
|
||||
g_object_bind_property (self, "title", self->label, "label", G_BINDING_DEFAULT);
|
||||
}
|
36
gtk/gtksimpletab.h
Normal file
36
gtk/gtksimpletab.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* gtksimpletab.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_SIMPLE_TAB_H__
|
||||
#define __GTK_SIMPLE_TAB_H__
|
||||
|
||||
#include <gtk/gtktab.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SIMPLE_TAB (gtk_simple_tab_get_type ())
|
||||
#define GTK_SIMPLE_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SIMPLE_TAB, GtkSimpleTab))
|
||||
#define GTK_IS_SIMPLE_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SIMPLE_TAB))
|
||||
|
||||
typedef struct _GtkSimpleTab GtkSimpleTab;
|
||||
|
||||
GType gtk_simple_tab_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SIMPLE_TAB_H__ */
|
519
gtk/gtktab.c
Normal file
519
gtk/gtktab.c
Normal 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);
|
||||
}
|
69
gtk/gtktab.h
Normal file
69
gtk/gtktab.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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);
|
||||
};
|
||||
|
||||
GType gtk_tab_get_type (void) G_GNUC_CONST;
|
||||
|
||||
const gchar *gtk_tab_get_title (GtkTab *self);
|
||||
void gtk_tab_set_title (GtkTab *self,
|
||||
const gchar *title);
|
||||
GtkWidget *gtk_tab_get_widget (GtkTab *self);
|
||||
void gtk_tab_set_widget (GtkTab *self,
|
||||
GtkWidget *widget);
|
||||
void gtk_tab_set_child (GtkTab *self,
|
||||
GtkWidget *child);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_TAB_H__ */
|
615
gtk/gtktabs.c
Normal file
615
gtk/gtktabs.c
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* Copyright 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 library 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 "gtktabsprivate.h"
|
||||
#include "gtkcontainerprivate.h"
|
||||
#include "gtkcsscustomgadgetprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcsscustomgadgetprivate.h"
|
||||
#include "a11y/gtkcontaineraccessible.h"
|
||||
#include "gtksettingsprivate.h"
|
||||
|
||||
typedef struct {
|
||||
GArray *children;
|
||||
GtkCssGadget *gadget;
|
||||
guint tick_id;
|
||||
} GtkTabsPrivate;
|
||||
|
||||
typedef struct {
|
||||
GtkWidget *child;
|
||||
gboolean animating;
|
||||
guint64 starttime;
|
||||
gdouble factor;
|
||||
gint width;
|
||||
} GtkTabsChild;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkTabs, gtk_tabs, GTK_TYPE_CONTAINER)
|
||||
|
||||
static void
|
||||
gtk_tabs_finalize (GObject *object)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (object));
|
||||
|
||||
g_clear_object (&priv->gadget);
|
||||
g_array_free (priv->children, TRUE);
|
||||
|
||||
G_OBJECT_CLASS (gtk_tabs_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tabs_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
|
||||
gtk_css_gadget_draw (priv->gadget, cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_size_allocate (GtkWidget *widget,
|
||||
GtkAllocation *allocation)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_get_preferred_width (GtkWidget *widget,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
-1,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_get_preferred_height (GtkWidget *widget,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
-1,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_get_preferred_width_for_height (GtkWidget *widget,
|
||||
gint height,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
height,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_get_preferred_height_for_width (GtkWidget *widget,
|
||||
gint width,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
width,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_direction_changed (GtkWidget *widget,
|
||||
GtkTextDirection previous_direction)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
int i, j;
|
||||
|
||||
gtk_css_node_reverse_children (gtk_widget_get_css_node (widget));
|
||||
|
||||
for (i = 0, j = priv->children->len - 1; i < j; i++, j--)
|
||||
{
|
||||
GtkTabsChild *child1 = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
GtkTabsChild *child2 = &g_array_index (priv->children, GtkTabsChild, j);
|
||||
GtkTabsChild tmp;
|
||||
|
||||
tmp = *child1;
|
||||
*child1 = *child2;
|
||||
*child2 = tmp;
|
||||
}
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_tabs_parent_class)->direction_changed (widget, previous_direction);
|
||||
}
|
||||
|
||||
#define DURATION (0.25 * 1e6)
|
||||
|
||||
static gdouble
|
||||
ease_out_cubic (gdouble t)
|
||||
{
|
||||
gdouble p = t - 1;
|
||||
return p * p * p + 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tabs_animate_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
int i;
|
||||
guint64 time;
|
||||
int animating;
|
||||
|
||||
animating = 0;
|
||||
time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
if (child->animating)
|
||||
{
|
||||
child->factor = ease_out_cubic ((time - child->starttime) / DURATION);
|
||||
if (child->factor >= 1.0)
|
||||
child->animating = FALSE;
|
||||
else
|
||||
animating += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = priv->children->len - 1; i >= 0; i--)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
if (child->child == NULL && !child->animating)
|
||||
g_array_remove_index (priv->children, i);
|
||||
}
|
||||
|
||||
gtk_widget_queue_allocate (widget);
|
||||
|
||||
if (animating == 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (widget, priv->tick_id);
|
||||
priv->tick_id = 0;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_tick_callback (GtkTabs *tabs)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
|
||||
if (priv->tick_id == 0)
|
||||
priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (tabs),
|
||||
gtk_tabs_animate_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_tabs_insert (GtkTabs *tabs,
|
||||
int pos,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
GtkTabsChild child;
|
||||
|
||||
child.child = widget;
|
||||
if (gtk_widget_get_mapped (GTK_WIDGET (tabs)) &&
|
||||
gtk_settings_get_enable_animations (gtk_widget_get_settings (GTK_WIDGET (tabs))))
|
||||
{
|
||||
child.animating = TRUE;
|
||||
child.starttime = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (GTK_WIDGET (tabs)));
|
||||
child.factor = 0.0;
|
||||
ensure_tick_callback (tabs);
|
||||
}
|
||||
else
|
||||
{
|
||||
child.animating = FALSE;
|
||||
child.starttime = 0;
|
||||
child.factor = 1.0;
|
||||
}
|
||||
|
||||
if (pos < 0 || pos >= priv->children->len)
|
||||
{
|
||||
g_array_append_val (priv->children, child);
|
||||
gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (tabs)),
|
||||
gtk_widget_get_css_node (widget),
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_array_insert_val (priv->children, pos, child);
|
||||
gtk_css_node_insert_before (gtk_widget_get_css_node (GTK_WIDGET (tabs)),
|
||||
gtk_widget_get_css_node (widget),
|
||||
gtk_widget_get_css_node (g_array_index (priv->children,
|
||||
GtkTabsChild,
|
||||
pos + 1).child));
|
||||
}
|
||||
|
||||
gtk_widget_set_parent (widget, GTK_WIDGET (tabs));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_add (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
gtk_tabs_insert (GTK_TABS (container), -1, widget);
|
||||
}
|
||||
|
||||
static GtkTabsChild *
|
||||
gtk_tabs_find_child (GtkTabs *tabs,
|
||||
GtkWidget *widget,
|
||||
int *position)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
if (child->child == widget)
|
||||
{
|
||||
if (position)
|
||||
*position = i;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_remove (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (container));
|
||||
GtkTabsChild *child;
|
||||
int position;
|
||||
|
||||
child = gtk_tabs_find_child (GTK_TABS (container), widget, &position);
|
||||
if (child)
|
||||
{
|
||||
gtk_widget_unparent (child->child);
|
||||
if (gtk_widget_get_mapped (GTK_WIDGET (container)) &&
|
||||
gtk_settings_get_enable_animations (gtk_widget_get_settings (GTK_WIDGET (container))))
|
||||
{
|
||||
child->child = NULL;
|
||||
child->animating = TRUE;
|
||||
child->starttime = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (GTK_WIDGET (container)));
|
||||
|
||||
ensure_tick_callback (GTK_TABS (container));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_array_remove_index (priv->children, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_forall (GtkContainer *container,
|
||||
gboolean include_internals,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (container));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
if (child->child)
|
||||
callback (child->child, callback_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_class_init (GtkTabsClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_tabs_finalize;
|
||||
|
||||
widget_class->draw = gtk_tabs_draw;
|
||||
widget_class->size_allocate = gtk_tabs_size_allocate;
|
||||
widget_class->get_preferred_width = gtk_tabs_get_preferred_width;
|
||||
widget_class->get_preferred_height = gtk_tabs_get_preferred_height;
|
||||
widget_class->get_preferred_height_for_width = gtk_tabs_get_preferred_height_for_width;
|
||||
widget_class->get_preferred_width_for_height = gtk_tabs_get_preferred_width_for_height;
|
||||
widget_class->direction_changed = gtk_tabs_direction_changed;
|
||||
|
||||
container_class->add = gtk_tabs_add;
|
||||
container_class->remove = gtk_tabs_remove;
|
||||
container_class->forall = gtk_tabs_forall;
|
||||
|
||||
gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_FILLER);
|
||||
}
|
||||
|
||||
static void
|
||||
measure_child (GtkTabsChild *child,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
int child_min, child_nat;
|
||||
|
||||
if (child->child)
|
||||
{
|
||||
_gtk_widget_get_preferred_size_for_size (child->child,
|
||||
orientation,
|
||||
for_size,
|
||||
&child_min,
|
||||
&child_nat,
|
||||
NULL, NULL);
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL && child->animating)
|
||||
{
|
||||
child_min = MAX ((int) (child->factor * child_min), 1);
|
||||
child_nat = MAX ((int) (child->factor * child_nat), 1);
|
||||
}
|
||||
}
|
||||
else if (child->animating)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
child_min = child_nat = MAX ((1.0 - child->factor) * child->width, 1);
|
||||
else
|
||||
child_min = child_nat = 1;
|
||||
}
|
||||
|
||||
*minimum = child_min;
|
||||
*natural = child_nat;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_measure_orientation (GtkCssGadget *gadget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
int child_min, child_nat;
|
||||
int i;
|
||||
|
||||
*minimum = 0;
|
||||
*natural = 0;
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
measure_child (child, orientation, for_size, &child_min, &child_nat);
|
||||
|
||||
*minimum += child_min;
|
||||
*natural += child_nat;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_distribute (GtkTabs *tabs,
|
||||
int for_size,
|
||||
int size,
|
||||
GtkRequestedSize *sizes)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
measure_child (child,
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
for_size,
|
||||
&sizes[i].minimum_size,
|
||||
&sizes[i].natural_size);
|
||||
|
||||
size -= sizes[i].minimum_size;
|
||||
}
|
||||
|
||||
size = gtk_distribute_natural_allocation (size, priv->children->len, sizes);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_measure_opposite (GtkCssGadget *gadget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
int child_min, child_nat;
|
||||
GtkRequestedSize *sizes;
|
||||
int i;
|
||||
|
||||
*minimum = 0;
|
||||
*natural = 0;
|
||||
|
||||
if (for_size >= 0)
|
||||
{
|
||||
sizes = g_newa (GtkRequestedSize, priv->children->len);
|
||||
gtk_tabs_distribute (GTK_TABS (widget), -1, for_size, sizes);
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
|
||||
measure_child (child,
|
||||
orientation,
|
||||
for_size >= 0 ? sizes[i].minimum_size : -1,
|
||||
&child_min, &child_nat);
|
||||
|
||||
*minimum = MAX (*minimum, child_min);
|
||||
*natural = MAX (*natural, child_nat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_measure (GtkCssGadget *gadget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline,
|
||||
gpointer unused)
|
||||
{
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
gtk_tabs_measure_orientation (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
else
|
||||
gtk_tabs_measure_opposite (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_allocate (GtkCssGadget *gadget,
|
||||
const GtkAllocation *allocation,
|
||||
int baseline,
|
||||
GtkAllocation *out_clip,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
GtkRequestedSize *sizes;
|
||||
GtkAllocation child_allocation, child_clip;
|
||||
int i;
|
||||
|
||||
child_allocation = *allocation;
|
||||
sizes = g_newa (GtkRequestedSize, priv->children->len);
|
||||
|
||||
gtk_tabs_distribute (GTK_TABS (widget), allocation->height, allocation->width, sizes);
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
child_allocation.width = sizes[i].minimum_size;
|
||||
child_allocation.height = allocation->height;
|
||||
if (!child->animating)
|
||||
{
|
||||
child->width = child_allocation.width;
|
||||
gtk_widget_size_allocate_with_baseline (child->child, &child_allocation, baseline);
|
||||
gtk_widget_get_clip (child->child, &child_clip);
|
||||
if (i == 0)
|
||||
*out_clip = child_clip;
|
||||
else
|
||||
gdk_rectangle_union (out_clip, &child_clip, out_clip);
|
||||
}
|
||||
child_allocation.x += sizes[i].minimum_size;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tabs_render (GtkCssGadget *gadget,
|
||||
cairo_t *cr,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
gpointer data)
|
||||
{
|
||||
GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (GTK_TABS (widget));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->children->len; i++)
|
||||
{
|
||||
GtkTabsChild *child = &g_array_index (priv->children, GtkTabsChild, i);
|
||||
if (!child->animating)
|
||||
gtk_container_propagate_draw (GTK_CONTAINER (widget), child->child, cr);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tabs_init (GtkTabs *tabs)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
GtkCssNode *widget_node;
|
||||
|
||||
gtk_widget_set_has_window (GTK_WIDGET (tabs), FALSE);
|
||||
|
||||
priv->children = g_array_new (FALSE, FALSE, sizeof (GtkTabsChild));
|
||||
|
||||
widget_node = gtk_widget_get_css_node (GTK_WIDGET (tabs));
|
||||
priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
|
||||
GTK_WIDGET (tabs),
|
||||
gtk_tabs_measure,
|
||||
gtk_tabs_allocate,
|
||||
gtk_tabs_render,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_tabs_reorder_child (GtkTabs *tabs,
|
||||
GtkWidget *widget,
|
||||
int position)
|
||||
{
|
||||
GtkTabsPrivate *priv = gtk_tabs_get_instance_private (tabs);
|
||||
GtkTabsChild *child;
|
||||
GtkTabsChild tmp;
|
||||
int old_pos;
|
||||
|
||||
child = gtk_tabs_find_child (tabs, widget, &old_pos);
|
||||
if (!child || position == old_pos)
|
||||
return;
|
||||
|
||||
tmp = *child;
|
||||
g_array_remove_index (priv->children, old_pos);
|
||||
g_array_insert_val (priv->children, position, tmp);
|
||||
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (tabs));
|
||||
}
|
70
gtk/gtktabsprivate.h
Normal file
70
gtk/gtktabsprivate.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* 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 library 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_TABS_H__
|
||||
#define __GTK_TABS_H__
|
||||
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkcontainer.h>
|
||||
#include <gtk/gtkcssgadgetprivate.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
#define GTK_TYPE_TABS (gtk_tabs_get_type ())
|
||||
#define GTK_TABS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TABS, GtkTabs))
|
||||
#define GTK_TABS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TABS, GtkTabsClass))
|
||||
#define GTK_IS_TABS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TABS))
|
||||
#define GTK_IS_TABS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TABS))
|
||||
#define GTK_TABS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TABS, GtkTabsClass))
|
||||
|
||||
|
||||
typedef struct _GtkTabs GtkTabs;
|
||||
typedef struct _GtkTabsClass GtkTabsClass;
|
||||
|
||||
struct _GtkTabs
|
||||
{
|
||||
GtkContainer container;
|
||||
};
|
||||
|
||||
struct _GtkTabsClass
|
||||
{
|
||||
GtkContainerClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType gtk_tabs_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void gtk_tabs_reorder_child (GtkTabs *tabs,
|
||||
GtkWidget *child,
|
||||
int position);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
756
gtk/gtktabstrip.c
Normal file
756
gtk/gtktabstrip.c
Normal file
@@ -0,0 +1,756 @@
|
||||
/* 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 "gtkclosabletab.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkscrolledwindow.h"
|
||||
#include "gtkbutton.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtktabsprivate.h"
|
||||
#include "gtkadjustmentprivate.h"
|
||||
#include "gtkboxgadgetprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkCssGadget *gadget;
|
||||
GtkStack *stack;
|
||||
gboolean closable;
|
||||
gboolean reversed;
|
||||
GtkWidget *scrolledwindow;
|
||||
GtkWidget *tabs;
|
||||
GtkScrollType autoscroll_mode;
|
||||
guint autoscroll_id;
|
||||
gboolean autoscroll_goal_set;
|
||||
gdouble autoscroll_goal;
|
||||
} GtkTabStripPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkTabStrip, gtk_tab_strip, GTK_TYPE_CONTAINER)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_STACK,
|
||||
PROP_CLOSABLE,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS];
|
||||
|
||||
static void
|
||||
gtk_tab_strip_add (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
g_warning ("Can't add children to %s", G_OBJECT_TYPE_NAME (container));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_remove (GtkContainer *container,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
g_warning ("Can't remove children from %s", G_OBJECT_TYPE_NAME (container));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_forall (GtkContainer *container,
|
||||
gboolean include_internals,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (container);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
if (include_internals)
|
||||
(*callback) (priv->scrolledwindow, callback_data);
|
||||
}
|
||||
|
||||
static GType
|
||||
gtk_tab_strip_child_type (GtkContainer *container)
|
||||
{
|
||||
return G_TYPE_NONE;
|
||||
}
|
||||
|
||||
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_preferred_width (GtkWidget *widget,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (widget);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
-1,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_get_preferred_height (GtkWidget *widget,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (widget);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
-1,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_get_preferred_width_for_height (GtkWidget *widget,
|
||||
gint height,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (widget);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_HORIZONTAL,
|
||||
height,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_get_preferred_height_for_width (GtkWidget *widget,
|
||||
gint width,
|
||||
gint *minimum_size,
|
||||
gint *natural_size)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (widget);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
gtk_css_gadget_get_preferred_size (priv->gadget,
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
width,
|
||||
minimum_size, natural_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_tab_strip_draw (GtkWidget *widget,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (widget);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
gtk_css_gadget_draw (priv->gadget, cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_node_ordering (GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
gboolean reverse;
|
||||
|
||||
reverse = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
|
||||
|
||||
if ((reverse && !priv->reversed) ||
|
||||
(!reverse && priv->reversed))
|
||||
{
|
||||
gtk_box_gadget_reverse_children (GTK_BOX_GADGET (priv->gadget));
|
||||
priv->reversed = reverse;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_direction_changed (GtkWidget *widget,
|
||||
GtkTextDirection previous_direction)
|
||||
{
|
||||
update_node_ordering (GTK_TAB_STRIP (widget));
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_tab_strip_parent_class)->direction_changed (widget, previous_direction);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case PROP_CLOSABLE:
|
||||
g_value_set_boolean (value, gtk_tab_strip_get_closable (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;
|
||||
|
||||
case PROP_CLOSABLE:
|
||||
gtk_tab_strip_set_closable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_finalize (GObject *object)
|
||||
{
|
||||
GtkTabStrip *self = GTK_TAB_STRIP (object);
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
g_clear_object (&priv->gadget);
|
||||
|
||||
G_OBJECT_CLASS (gtk_tab_strip_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
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;
|
||||
object_class->finalize = gtk_tab_strip_finalize;
|
||||
|
||||
widget_class->destroy = gtk_tab_strip_destroy;
|
||||
widget_class->get_preferred_width = gtk_tab_strip_get_preferred_width;
|
||||
widget_class->get_preferred_height = gtk_tab_strip_get_preferred_height;
|
||||
widget_class->get_preferred_width_for_height = gtk_tab_strip_get_preferred_width_for_height;
|
||||
widget_class->get_preferred_height_for_width = gtk_tab_strip_get_preferred_height_for_width;
|
||||
widget_class->size_allocate = gtk_tab_strip_size_allocate;
|
||||
widget_class->draw = gtk_tab_strip_draw;
|
||||
widget_class->direction_changed = gtk_tab_strip_direction_changed;
|
||||
|
||||
container_class->add = gtk_tab_strip_add;
|
||||
container_class->remove = gtk_tab_strip_remove;
|
||||
container_class->forall = gtk_tab_strip_forall;
|
||||
container_class->child_type = gtk_tab_strip_child_type;
|
||||
|
||||
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);
|
||||
|
||||
properties[PROP_CLOSABLE] =
|
||||
g_param_spec_boolean ("closable", P_("Closable"), P_("Whether tabs can be closed"),
|
||||
FALSE,
|
||||
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
|
||||
update_scrolling (GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkPolicyType hscroll, vscroll;
|
||||
|
||||
hscroll = GTK_POLICY_EXTERNAL;
|
||||
vscroll = GTK_POLICY_NEVER;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
|
||||
hscroll, vscroll);
|
||||
}
|
||||
|
||||
static void
|
||||
update_needs_attention_overflow (GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkAdjustment *adj;
|
||||
gdouble value, page_size;
|
||||
GList *tabs, *l;
|
||||
GtkCssNode *scroll_node, *node;
|
||||
gboolean needs_attention_below = FALSE;
|
||||
gboolean needs_attention_above = FALSE;
|
||||
|
||||
adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
|
||||
value = gtk_adjustment_get_value (adj);
|
||||
page_size = gtk_adjustment_get_page_size (adj);
|
||||
|
||||
tabs = gtk_container_get_children (GTK_CONTAINER (priv->tabs));
|
||||
for (l = tabs; l; l = l->next)
|
||||
{
|
||||
GtkWidget *tab = l->data;
|
||||
GtkStyleContext *context;
|
||||
GtkAllocation tab_alloc;
|
||||
|
||||
context = gtk_widget_get_style_context (tab);
|
||||
if (!gtk_style_context_has_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION))
|
||||
continue;
|
||||
|
||||
gtk_widget_get_allocation (tab, &tab_alloc);
|
||||
if (tab_alloc.x + tab_alloc.width < value)
|
||||
needs_attention_below = TRUE;
|
||||
else if (tab_alloc.x >= value + page_size)
|
||||
needs_attention_above = TRUE;
|
||||
}
|
||||
g_list_free (tabs);
|
||||
|
||||
scroll_node = gtk_widget_get_css_node (priv->scrolledwindow);
|
||||
for (node = gtk_css_node_get_first_child (scroll_node);
|
||||
node;
|
||||
node = gtk_css_node_get_next_sibling (node))
|
||||
{
|
||||
if (strcmp (gtk_css_node_get_name (node), "undershoot") != 0)
|
||||
continue;
|
||||
|
||||
if (gtk_css_node_has_class (node, g_quark_from_static_string ("left")))
|
||||
{
|
||||
if (needs_attention_below)
|
||||
gtk_css_node_add_class (node, g_quark_from_static_string (GTK_STYLE_CLASS_NEEDS_ATTENTION));
|
||||
else
|
||||
gtk_css_node_remove_class (node, g_quark_from_static_string (GTK_STYLE_CLASS_NEEDS_ATTENTION));
|
||||
}
|
||||
else if (gtk_css_node_has_class (node, g_quark_from_static_string ("right")))
|
||||
{
|
||||
if (needs_attention_above)
|
||||
gtk_css_node_add_class (node, g_quark_from_static_string (GTK_STYLE_CLASS_NEEDS_ATTENTION));
|
||||
else
|
||||
gtk_css_node_remove_class (node, g_quark_from_static_string (GTK_STYLE_CLASS_NEEDS_ATTENTION));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_init (GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkCssNode *widget_node;
|
||||
GtkAdjustment *adj;
|
||||
|
||||
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
|
||||
|
||||
priv->closable = 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_orientation (GTK_BOX_GADGET (priv->gadget), GTK_ORIENTATION_HORIZONTAL);
|
||||
|
||||
priv->scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_show (priv->scrolledwindow);
|
||||
gtk_widget_set_parent (priv->scrolledwindow, GTK_WIDGET (self));
|
||||
gtk_box_gadget_insert_widget (GTK_BOX_GADGET (priv->gadget), 1, priv->scrolledwindow);
|
||||
gtk_box_gadget_set_gadget_expand (GTK_BOX_GADGET (priv->gadget), G_OBJECT (priv->scrolledwindow), TRUE);
|
||||
update_scrolling (self);
|
||||
|
||||
priv->tabs = g_object_new (GTK_TYPE_TABS, NULL);
|
||||
gtk_widget_show (priv->tabs);
|
||||
gtk_container_add (GTK_CONTAINER (priv->scrolledwindow), priv->tabs);
|
||||
|
||||
adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
|
||||
g_signal_connect_swapped (adj, "changed", G_CALLBACK (update_needs_attention_overflow), self);
|
||||
g_signal_connect_swapped (adj, "value-changed", G_CALLBACK (update_needs_attention_overflow), self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_child_position_changed (GtkTabStrip *self,
|
||||
GParamSpec *pspec,
|
||||
GtkWidget *child)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
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_tabs_reorder_child (GTK_TABS (priv->tabs), GTK_WIDGET (tab), position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_child_needs_attention_changed (GtkTabStrip *self,
|
||||
GParamSpec *pspec,
|
||||
GtkWidget *child)
|
||||
{
|
||||
GtkWidget *parent;
|
||||
GtkWidget *tab;
|
||||
gboolean needs_attention;
|
||||
GtkStyleContext *context;
|
||||
|
||||
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), GTK_WIDGET (child),
|
||||
"needs-attention", &needs_attention,
|
||||
NULL);
|
||||
|
||||
context = gtk_widget_get_style_context (tab);
|
||||
if (needs_attention)
|
||||
gtk_style_context_add_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
|
||||
else
|
||||
gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
|
||||
|
||||
update_needs_attention_overflow (self);
|
||||
}
|
||||
|
||||
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
|
||||
scroll_to_tab (GtkTabStrip *self,
|
||||
GtkWidget *tab)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkAllocation tab_alloc;
|
||||
GtkAdjustment *adj;
|
||||
gdouble value, page_size;
|
||||
|
||||
gtk_widget_get_allocation (tab, &tab_alloc);
|
||||
|
||||
adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
|
||||
value = gtk_adjustment_get_value (adj);
|
||||
page_size = gtk_adjustment_get_page_size (adj);
|
||||
|
||||
if (tab_alloc.x < value)
|
||||
gtk_adjustment_animate_to_value (adj, tab_alloc.x);
|
||||
else if (tab_alloc.x + tab_alloc.width >= value + page_size)
|
||||
gtk_adjustment_animate_to_value (adj, tab_alloc.x + tab_alloc.width - page_size);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
scroll_to_visible_tab (gpointer data)
|
||||
{
|
||||
GtkTabStrip *self = data;
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkWidget *child;
|
||||
|
||||
child = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
|
||||
if (child)
|
||||
{
|
||||
GtkWidget *tab;
|
||||
|
||||
tab = (GtkWidget *)g_object_get_data (G_OBJECT (child), "GTK_TAB");
|
||||
scroll_to_tab (self, tab);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
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 *child;
|
||||
|
||||
child = gtk_stack_get_visible_child (stack);
|
||||
gtk_container_foreach (GTK_CONTAINER (priv->tabs), update_visible_child, child);
|
||||
g_idle_add (scroll_to_visible_tab, self);
|
||||
}
|
||||
|
||||
static void
|
||||
tab_activated (GtkTab *tab,
|
||||
GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkWidget *widget;
|
||||
GtkWidget *visible_child;
|
||||
|
||||
widget = gtk_tab_get_widget (tab);
|
||||
if (widget == NULL)
|
||||
return;
|
||||
|
||||
visible_child = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
|
||||
if (widget == visible_child)
|
||||
scroll_to_tab (self, GTK_WIDGET (tab));
|
||||
else
|
||||
gtk_stack_set_visible_child (priv->stack, widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tab_strip_stack_add (GtkTabStrip *self,
|
||||
GtkWidget *widget,
|
||||
GtkStack *stack)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkTab *tab;
|
||||
gint position = 0;
|
||||
|
||||
gtk_container_child_get (GTK_CONTAINER (stack), widget,
|
||||
"position", &position,
|
||||
NULL);
|
||||
|
||||
tab = g_object_new (priv->closable ? GTK_TYPE_CLOSABLE_TAB : 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);
|
||||
|
||||
g_signal_connect_object (widget, "child-notify::needs-attention",
|
||||
G_CALLBACK (gtk_tab_strip_child_needs_attention_changed), self,
|
||||
G_CONNECT_SWAPPED);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (priv->tabs), GTK_WIDGET (tab));
|
||||
|
||||
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)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
GtkTab *tab;
|
||||
|
||||
tab = g_object_get_data (G_OBJECT (widget), "GTK_TAB");
|
||||
|
||||
if (GTK_IS_TAB (tab))
|
||||
gtk_container_remove (GTK_CONTAINER (priv->tabs), 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]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_tab_strip_set_closable (GtkTabStrip *self,
|
||||
gboolean closable)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
g_return_if_fail (GTK_IS_TAB_STRIP (self));
|
||||
|
||||
if (priv->closable == closable)
|
||||
return;
|
||||
|
||||
priv->closable = closable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CLOSABLE]);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_tab_strip_get_closable (GtkTabStrip *self)
|
||||
{
|
||||
GtkTabStripPrivate *priv = gtk_tab_strip_get_instance_private (self);
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TAB_STRIP (self), FALSE);
|
||||
|
||||
return priv->closable;
|
||||
}
|
79
gtk/gtktabstrip.h
Normal file
79
gtk/gtktabstrip.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* 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/gtkcontainer.h>
|
||||
#include <gtk/gtkstack.h>
|
||||
#include <gtk/gtktab.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
|
||||
{
|
||||
GtkContainer parent;
|
||||
};
|
||||
|
||||
struct _GtkTabStripClass
|
||||
{
|
||||
GtkContainerClass parent_class;
|
||||
|
||||
/* 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_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);
|
||||
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,
|
||||
gboolean closable);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_TAB_STRIP_H__ */
|
@@ -2288,6 +2288,39 @@ notebook {
|
||||
}
|
||||
}
|
||||
|
||||
/************
|
||||
* Tabstrip *
|
||||
************/
|
||||
|
||||
tabs {
|
||||
button.flat.image-button {
|
||||
@include button(undecorated);
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
&:hover {
|
||||
@include button(hover);
|
||||
color: currentColor;
|
||||
-gtk-icon-effect: highlight;
|
||||
}
|
||||
&, &:backdrop { color: gtkalpha(currentColor, 0.3); }
|
||||
}
|
||||
tab {
|
||||
min-width: 6em;
|
||||
padding: 3px; // keeping width when paging buttons show up
|
||||
border-right: 1px solid transparentize($borders_color,0.5);
|
||||
&:first-child { border-left: 1px solid transparentize($borders_color,0.5); }
|
||||
&:checked,
|
||||
&:checked:hover { //FIXME not really working
|
||||
border-bottom: 2px solid $selected_bg_color;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
&:hover {
|
||||
border-bottom: 2px solid $borders_color;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************
|
||||
* Scrollbars *
|
||||
|
@@ -2570,6 +2570,44 @@ notebook > stack:not(:only-child) {
|
||||
notebook > stack:not(:only-child):backdrop {
|
||||
background-color: #252a2c; }
|
||||
|
||||
/************
|
||||
* Tabstrip *
|
||||
************/
|
||||
tabs button.flat.image-button {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
box-shadow: inset 0 1px rgba(255, 255, 255, 0);
|
||||
text-shadow: none;
|
||||
-gtk-icon-shadow: none;
|
||||
margin: 10px;
|
||||
padding: 0; }
|
||||
tabs button.flat.image-button:hover {
|
||||
color: #eeeeec;
|
||||
outline-color: rgba(238, 238, 236, 0.3);
|
||||
border-color: #1b1f20;
|
||||
border-bottom-color: #0b0c0c;
|
||||
background-image: linear-gradient(to bottom, #353c3e, #2e3436 60%, #2a2e30);
|
||||
text-shadow: 0 -1px rgba(0, 0, 0, 0.77945);
|
||||
-gtk-icon-shadow: 0 -1px rgba(0, 0, 0, 0.77945);
|
||||
box-shadow: inset 0 1px rgba(255, 255, 255, 0.05);
|
||||
color: currentColor;
|
||||
-gtk-icon-effect: highlight; }
|
||||
tabs button.flat.image-button, tabs button.flat.image-button:backdrop {
|
||||
color: alpha(currentColor,0.3); }
|
||||
tabs tab {
|
||||
min-width: 6em;
|
||||
padding: 3px;
|
||||
border-right: 1px solid rgba(27, 31, 32, 0.5); }
|
||||
tabs tab:first-child {
|
||||
border-left: 1px solid rgba(27, 31, 32, 0.5); }
|
||||
tabs tab:checked, tabs tab:checked:hover {
|
||||
border-bottom: 2px solid #215d9c;
|
||||
padding-bottom: 1px; }
|
||||
tabs tab:hover {
|
||||
border-bottom: 2px solid #1b1f20;
|
||||
padding-bottom: 1px; }
|
||||
|
||||
/**************
|
||||
* Scrollbars *
|
||||
**************/
|
||||
|
@@ -2583,6 +2583,44 @@ notebook > stack:not(:only-child) {
|
||||
notebook > stack:not(:only-child):backdrop {
|
||||
background-color: #fcfcfc; }
|
||||
|
||||
/************
|
||||
* Tabstrip *
|
||||
************/
|
||||
tabs button.flat.image-button {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
box-shadow: inset 0 1px rgba(255, 255, 255, 0);
|
||||
text-shadow: none;
|
||||
-gtk-icon-shadow: none;
|
||||
margin: 10px;
|
||||
padding: 0; }
|
||||
tabs button.flat.image-button:hover {
|
||||
color: #2e3436;
|
||||
outline-color: rgba(46, 52, 54, 0.3);
|
||||
border-color: #b6b6b3;
|
||||
border-bottom-color: #91918c;
|
||||
background-image: linear-gradient(to bottom, #f7f7f7, #e8e8e7 60%, #dededd);
|
||||
text-shadow: 0 1px rgba(255, 255, 255, 0.76923);
|
||||
-gtk-icon-shadow: 0 1px rgba(255, 255, 255, 0.76923);
|
||||
box-shadow: inset 0 1px white;
|
||||
color: currentColor;
|
||||
-gtk-icon-effect: highlight; }
|
||||
tabs button.flat.image-button, tabs button.flat.image-button:backdrop {
|
||||
color: alpha(currentColor,0.3); }
|
||||
tabs tab {
|
||||
min-width: 6em;
|
||||
padding: 3px;
|
||||
border-right: 1px solid rgba(182, 182, 179, 0.5); }
|
||||
tabs tab:first-child {
|
||||
border-left: 1px solid rgba(182, 182, 179, 0.5); }
|
||||
tabs tab:checked, tabs tab:checked:hover {
|
||||
border-bottom: 2px solid #4a90d9;
|
||||
padding-bottom: 1px; }
|
||||
tabs tab:hover {
|
||||
border-bottom: 2px solid #b6b6b3;
|
||||
padding-bottom: 1px; }
|
||||
|
||||
/**************
|
||||
* Scrollbars *
|
||||
**************/
|
||||
|
@@ -172,6 +172,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
|
||||
gdkgears \
|
||||
listmodel \
|
||||
testpopup \
|
||||
testtabstrip \
|
||||
$(NULL)
|
||||
|
||||
if USE_X11
|
||||
|
152
tests/testtabstrip.c
Normal file
152
tests/testtabstrip.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static int count = 1;
|
||||
|
||||
static GtkWidget *
|
||||
make_child (const char *title)
|
||||
{
|
||||
GtkWidget *sw, *tv;
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
tv = gtk_text_view_new ();
|
||||
gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv)), title, -1);
|
||||
g_object_set (tv, "expand", TRUE, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (sw), tv);
|
||||
gtk_widget_show_all (sw);
|
||||
|
||||
return sw;
|
||||
}
|
||||
|
||||
static void
|
||||
add_child (GtkWidget *stack,
|
||||
const char *title)
|
||||
{
|
||||
char *name;
|
||||
GtkWidget *child;
|
||||
|
||||
name = g_strdup_printf ("tab%d", count++);
|
||||
|
||||
child = make_child (title);
|
||||
gtk_stack_add_titled (GTK_STACK (stack), child, name, title);
|
||||
gtk_stack_set_visible_child (GTK_STACK (stack), child);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
add_stack_child (GtkWidget *stack)
|
||||
{
|
||||
GtkWidget *child;
|
||||
char *name;
|
||||
char *title;
|
||||
|
||||
name = g_strdup_printf ("tab%d", count++);
|
||||
title = g_strdup_printf ("Page %d", count);
|
||||
|
||||
child = make_child (title);
|
||||
gtk_container_add_with_properties (GTK_CONTAINER (stack),
|
||||
child,
|
||||
"name", name,
|
||||
"title", title,
|
||||
"position", 3,
|
||||
NULL);
|
||||
|
||||
g_free (title);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
populate (GtkWidget *stack)
|
||||
{
|
||||
add_child (stack, "Enigma machine - Wikipedia, the free ecyclopedia");
|
||||
add_child (stack, "Ada Lovelace");
|
||||
add_child (stack, "Analytical Engine | computer | Britannica.com");
|
||||
add_child (stack, "Charles Babbage");
|
||||
add_child (stack, "Turing Machine — from Wolfram MathWorld");
|
||||
add_child (stack, "Enigma machine - Wikipedia, the free ecyclopedia");
|
||||
add_child (stack, "Enigma machine - Wikipedia, the free ecyclopedia");
|
||||
add_child (stack, "Enigma machine - Wikipedia, the free ecyclopedia");
|
||||
add_child (stack, "Enigma machine - Wikipedia, the free ecyclopedia");
|
||||
}
|
||||
|
||||
static void
|
||||
attention (GtkButton *button, GtkStack *stack)
|
||||
{
|
||||
GList *children;
|
||||
children = gtk_container_get_children (GTK_CONTAINER (stack));
|
||||
gtk_container_child_set (GTK_CONTAINER (stack), children->data,
|
||||
"needs-attention", TRUE,
|
||||
NULL);
|
||||
gtk_container_child_set (GTK_CONTAINER (stack), g_list_last (children)->data,
|
||||
"needs-attention", TRUE,
|
||||
NULL);
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
static const char css[] =
|
||||
"tab.needs-attention { border-bottom: solid 4px red; }"
|
||||
"scrolledwindow undershoot.left.needs-attention { background: none; border-left: solid 4px red; }"
|
||||
"scrolledwindow undershoot.right.needs-attention { background: none; border-right: solid 4px red; }";
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *titlebar;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *tabs;
|
||||
GtkWidget *stack;
|
||||
GtkWidget *button;
|
||||
GtkWidget *hbox;
|
||||
GtkCssProvider *provider;
|
||||
|
||||
gtk_init (NULL, NULL);
|
||||
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider, css, -1, NULL);
|
||||
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
|
||||
GTK_STYLE_PROVIDER (provider),
|
||||
800);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 800);
|
||||
titlebar = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (titlebar), TRUE);
|
||||
gtk_widget_show (titlebar);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Tab Strip Test");
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (window), vbox);
|
||||
|
||||
stack = gtk_stack_new ();
|
||||
g_object_set (stack, "expand", TRUE, NULL);
|
||||
|
||||
tabs = gtk_tab_strip_new ();
|
||||
gtk_tab_strip_set_closable (GTK_TAB_STRIP (tabs), TRUE);
|
||||
gtk_tab_strip_set_stack (GTK_TAB_STRIP (tabs), GTK_STACK (stack));
|
||||
|
||||
button = gtk_button_new_from_icon_name ("tab-new-symbolic", GTK_ICON_SIZE_BUTTON);
|
||||
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (titlebar), button);
|
||||
g_signal_connect_swapped (button, "clicked",
|
||||
G_CALLBACK (add_stack_child), stack);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (vbox), tabs, FALSE, FALSE, 0);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), stack, TRUE, TRUE, 0);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
button = gtk_button_new_with_label ("Attention");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (attention), stack);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
||||
|
||||
populate (stack);
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user