stacksidebar: Inherit from GtkWidget

The child widgets are completely managed by the stacksidebar itself, so
this has no business being a GtkBin.
This commit is contained in:
Timm Bäder 2019-10-06 13:46:05 +02:00
parent 553c458e5a
commit 3d3525a589
2 changed files with 96 additions and 113 deletions

View File

@ -23,12 +23,14 @@
#include "gtkstacksidebar.h"
#include "gtkbinlayout.h"
#include "gtklabel.h"
#include "gtklistbox.h"
#include "gtkscrolledwindow.h"
#include "gtkseparator.h"
#include "gtkstylecontext.h"
#include "gtkselectionmodel.h"
#include "gtkstack.h"
#include "gtkprivate.h"
#include "gtkintl.h"
@ -55,28 +57,24 @@
* pages.
*/
typedef struct _GtkStackSidebarPrivate GtkStackSidebarPrivate;
typedef struct _GtkStackSidebarClass GtkStackSidebarClass;
struct _GtkStackSidebar
{
GtkBin parent;
};
GtkWidget parent_instance;
struct _GtkStackSidebarClass
{
GtkBinClass parent_class;
};
struct _GtkStackSidebarPrivate
{
GtkListBox *list;
GtkStack *stack;
GtkSelectionModel *pages;
GHashTable *rows;
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkStackSidebar, gtk_stack_sidebar, GTK_TYPE_BIN)
struct _GtkStackSidebarClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkStackSidebar, gtk_stack_sidebar, GTK_TYPE_WIDGET)
enum
{
@ -110,12 +108,12 @@ gtk_stack_sidebar_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (GTK_STACK_SIDEBAR (object));
GtkStackSidebar *self = GTK_STACK_SIDEBAR (object);
switch (prop_id)
{
case PROP_STACK:
g_value_set_object (value, priv->stack);
g_value_set_object (value, self->stack);
break;
default:
@ -143,50 +141,46 @@ gtk_stack_sidebar_row_selected (GtkListBox *box,
GtkListBoxRow *row,
gpointer userdata)
{
GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (userdata);
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
GtkStackSidebar *self = GTK_STACK_SIDEBAR (userdata);
guint index;
if (row == NULL)
return;
index = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "child-index"));
gtk_selection_model_select_item (priv->pages, index, TRUE);
gtk_selection_model_select_item (self->pages, index, TRUE);
}
static void
gtk_stack_sidebar_init (GtkStackSidebar *sidebar)
gtk_stack_sidebar_init (GtkStackSidebar *self)
{
GtkStyleContext *style;
GtkStackSidebarPrivate *priv;
GtkWidget *sw;
priv = gtk_stack_sidebar_get_instance_private (sidebar);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_NEVER,
GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (sidebar), sw);
gtk_widget_set_parent (sw, GTK_WIDGET (self));
priv->list = GTK_LIST_BOX (gtk_list_box_new ());
self->list = GTK_LIST_BOX (gtk_list_box_new ());
gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->list));
gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (self->list));
gtk_list_box_set_header_func (priv->list, update_header, sidebar, NULL);
gtk_list_box_set_header_func (self->list, update_header, self, NULL);
g_signal_connect (priv->list, "row-selected",
G_CALLBACK (gtk_stack_sidebar_row_selected), sidebar);
g_signal_connect (self->list, "row-selected",
G_CALLBACK (gtk_stack_sidebar_row_selected), self);
style = gtk_widget_get_style_context (GTK_WIDGET (sidebar));
style = gtk_widget_get_style_context (GTK_WIDGET (self));
gtk_style_context_add_class (style, "sidebar");
priv->rows = g_hash_table_new (NULL, NULL);
self->rows = g_hash_table_new (NULL, NULL);
}
static void
update_row (GtkStackSidebar *sidebar,
update_row (GtkStackSidebar *self,
GtkStackPage *page,
GtkWidget *row)
{
@ -219,20 +213,18 @@ update_row (GtkStackSidebar *sidebar,
static void
on_page_updated (GtkStackPage *page,
GParamSpec *pspec,
GtkStackSidebar *sidebar)
GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
GtkWidget *row;
row = g_hash_table_lookup (priv->rows, page);
update_row (sidebar, page, row);
row = g_hash_table_lookup (self->rows, page);
update_row (self, page, row);
}
static void
add_child (guint position,
GtkStackSidebar *sidebar)
GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
GtkWidget *item;
GtkWidget *row;
GtkStackPage *page;
@ -244,48 +236,46 @@ add_child (guint position,
row = gtk_list_box_row_new ();
gtk_container_add (GTK_CONTAINER (row), item);
page = g_list_model_get_item (G_LIST_MODEL (priv->pages), position);
update_row (sidebar, page, row);
page = g_list_model_get_item (G_LIST_MODEL (self->pages), position);
update_row (self, page, row);
gtk_container_add (GTK_CONTAINER (priv->list), row);
gtk_container_add (GTK_CONTAINER (self->list), row);
g_object_set_data (G_OBJECT (row), "child-index", GUINT_TO_POINTER (position));
if (gtk_selection_model_is_selected (priv->pages, position))
gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row));
if (gtk_selection_model_is_selected (self->pages, position))
gtk_list_box_select_row (self->list, GTK_LIST_BOX_ROW (row));
else
gtk_list_box_unselect_row (priv->list, GTK_LIST_BOX_ROW (row));
gtk_list_box_unselect_row (self->list, GTK_LIST_BOX_ROW (row));
g_signal_connect (page, "notify", G_CALLBACK (on_page_updated), sidebar);
g_signal_connect (page, "notify", G_CALLBACK (on_page_updated), self);
g_hash_table_insert (priv->rows, page, row);
g_hash_table_insert (self->rows, page, row);
g_object_unref (page);
}
static void
populate_sidebar (GtkStackSidebar *sidebar)
populate_sidebar (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
guint i;
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (priv->pages)); i++)
add_child (i, sidebar);
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->pages)); i++)
add_child (i, self);
}
static void
clear_sidebar (GtkStackSidebar *sidebar)
clear_sidebar (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
GHashTableIter iter;
GtkStackPage *page;
GtkWidget *row;
g_hash_table_iter_init (&iter, priv->rows);
g_hash_table_iter_init (&iter, self->rows);
while (g_hash_table_iter_next (&iter, (gpointer *)&page, (gpointer *)&row))
{
gtk_container_remove (GTK_CONTAINER (priv->list), row);
gtk_container_remove (GTK_CONTAINER (self->list), row);
g_hash_table_iter_remove (&iter);
g_signal_handlers_disconnect_by_func (page, on_page_updated, sidebar);
g_signal_handlers_disconnect_by_func (page, on_page_updated, self);
}
}
@ -294,20 +284,19 @@ items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkStackSidebar *sidebar)
GtkStackSidebar *self)
{
/* FIXME: we can do better */
clear_sidebar (sidebar);
populate_sidebar (sidebar);
clear_sidebar (self);
populate_sidebar (self);
}
static void
selection_changed_cb (GtkSelectionModel *model,
guint position,
guint n_items,
GtkStackSidebar *sidebar)
GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
guint i;
for (i = position; i < position + n_items; i++)
@ -315,69 +304,70 @@ selection_changed_cb (GtkSelectionModel *model,
GtkStackPage *page;
GtkWidget *row;
page = g_list_model_get_item (G_LIST_MODEL (priv->pages), i);
row = g_hash_table_lookup (priv->rows, page);
if (gtk_selection_model_is_selected (priv->pages, i))
gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row));
page = g_list_model_get_item (G_LIST_MODEL (self->pages), i);
row = g_hash_table_lookup (self->rows, page);
if (gtk_selection_model_is_selected (self->pages, i))
gtk_list_box_select_row (self->list, GTK_LIST_BOX_ROW (row));
else
gtk_list_box_unselect_row (priv->list, GTK_LIST_BOX_ROW (row));
gtk_list_box_unselect_row (self->list, GTK_LIST_BOX_ROW (row));
g_object_unref (page);
}
}
static void
disconnect_stack_signals (GtkStackSidebar *sidebar)
disconnect_stack_signals (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
g_signal_handlers_disconnect_by_func (priv->pages, items_changed_cb, sidebar);
g_signal_handlers_disconnect_by_func (priv->pages, selection_changed_cb, sidebar);
g_signal_handlers_disconnect_by_func (self->pages, items_changed_cb, self);
g_signal_handlers_disconnect_by_func (self->pages, selection_changed_cb, self);
}
static void
connect_stack_signals (GtkStackSidebar *sidebar)
connect_stack_signals (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
g_signal_connect (priv->pages, "items-changed", G_CALLBACK (items_changed_cb), sidebar);
g_signal_connect (priv->pages, "selection-changed", G_CALLBACK (selection_changed_cb), sidebar);
g_signal_connect (self->pages, "items-changed", G_CALLBACK (items_changed_cb), self);
g_signal_connect (self->pages, "selection-changed", G_CALLBACK (selection_changed_cb), self);
}
static void
set_stack (GtkStackSidebar *sidebar,
set_stack (GtkStackSidebar *self,
GtkStack *stack)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
if (stack)
{
priv->stack = g_object_ref (stack);
priv->pages = gtk_stack_get_pages (stack);
populate_sidebar (sidebar);
connect_stack_signals (sidebar);
self->stack = g_object_ref (stack);
self->pages = gtk_stack_get_pages (stack);
populate_sidebar (self);
connect_stack_signals (self);
}
}
static void
unset_stack (GtkStackSidebar *sidebar)
unset_stack (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
if (priv->stack)
if (self->stack)
{
disconnect_stack_signals (sidebar);
clear_sidebar (sidebar);
g_clear_object (&priv->stack);
g_clear_object (&priv->pages);
disconnect_stack_signals (self);
clear_sidebar (self);
g_clear_object (&self->stack);
g_clear_object (&self->pages);
}
}
static void
gtk_stack_sidebar_dispose (GObject *object)
{
GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (object);
GtkStackSidebar *self = GTK_STACK_SIDEBAR (object);
GtkWidget *child;
unset_stack (sidebar);
unset_stack (self);
/* The scrolled window */
child = gtk_widget_get_first_child (GTK_WIDGET (self));
if (child)
{
gtk_widget_unparent (child);
self->list = NULL;
}
G_OBJECT_CLASS (gtk_stack_sidebar_parent_class)->dispose (object);
}
@ -385,10 +375,9 @@ gtk_stack_sidebar_dispose (GObject *object)
static void
gtk_stack_sidebar_finalize (GObject *object)
{
GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (object);
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
GtkStackSidebar *self = GTK_STACK_SIDEBAR (object);
g_hash_table_destroy (priv->rows);
g_hash_table_destroy (self->rows);
G_OBJECT_CLASS (gtk_stack_sidebar_parent_class)->finalize (object);
}
@ -412,6 +401,7 @@ gtk_stack_sidebar_class_init (GtkStackSidebarClass *klass)
g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_css_name (widget_class, I_("stacksidebar"));
}
@ -430,7 +420,7 @@ gtk_stack_sidebar_new (void)
/**
* gtk_stack_sidebar_set_stack:
* @sidebar: a #GtkStackSidebar
* @self: a #GtkStackSidebar
* @stack: a #GtkStack
*
* Set the #GtkStack associated with this #GtkStackSidebar.
@ -439,29 +429,27 @@ gtk_stack_sidebar_new (void)
* (packing) and items within the given #GtkStack.
*/
void
gtk_stack_sidebar_set_stack (GtkStackSidebar *sidebar,
gtk_stack_sidebar_set_stack (GtkStackSidebar *self,
GtkStack *stack)
{
GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar);
g_return_if_fail (GTK_IS_STACK_SIDEBAR (sidebar));
g_return_if_fail (GTK_IS_STACK_SIDEBAR (self));
g_return_if_fail (GTK_IS_STACK (stack) || stack == NULL);
if (priv->stack == stack)
if (self->stack == stack)
return;
unset_stack (sidebar);
set_stack (sidebar, stack);
unset_stack (self);
set_stack (self, stack);
gtk_widget_queue_resize (GTK_WIDGET (sidebar));
gtk_widget_queue_resize (GTK_WIDGET (self));
g_object_notify (G_OBJECT (sidebar), "stack");
g_object_notify (G_OBJECT (self), "stack");
}
/**
* gtk_stack_sidebar_get_stack:
* @sidebar: a #GtkStackSidebar
* @self: a #GtkStackSidebar
*
* Retrieves the stack.
* See gtk_stack_sidebar_set_stack().
@ -470,13 +458,9 @@ gtk_stack_sidebar_set_stack (GtkStackSidebar *sidebar,
* %NULL if none has been set explicitly
*/
GtkStack *
gtk_stack_sidebar_get_stack (GtkStackSidebar *sidebar)
gtk_stack_sidebar_get_stack (GtkStackSidebar *self)
{
GtkStackSidebarPrivate *priv;
g_return_val_if_fail (GTK_IS_STACK_SIDEBAR (self), NULL);
g_return_val_if_fail (GTK_IS_STACK_SIDEBAR (sidebar), NULL);
priv = gtk_stack_sidebar_get_instance_private (sidebar);
return GTK_STACK (priv->stack);
return GTK_STACK (self->stack);
}

View File

@ -26,7 +26,6 @@
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkbin.h>
#include <gtk/gtkstack.h>
G_BEGIN_DECLS
@ -42,10 +41,10 @@ GType gtk_stack_sidebar_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_stack_sidebar_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_stack_sidebar_set_stack (GtkStackSidebar *sidebar,
void gtk_stack_sidebar_set_stack (GtkStackSidebar *self,
GtkStack *stack);
GDK_AVAILABLE_IN_ALL
GtkStack * gtk_stack_sidebar_get_stack (GtkStackSidebar *sidebar);
GtkStack * gtk_stack_sidebar_get_stack (GtkStackSidebar *self);
G_END_DECLS