424 lines
12 KiB
C
424 lines
12 KiB
C
|
#include <gtk/gtk.h>
|
||
|
|
||
|
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||
|
G_DECLARE_FINAL_TYPE (TestObject, test_object, TEST, OBJECT, GObject)
|
||
|
|
||
|
struct _TestObject {
|
||
|
GObject parent_instance;
|
||
|
char *string;
|
||
|
guint number;
|
||
|
gboolean allow_children;
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
PROP_STRING = 1,
|
||
|
PROP_NUMBER,
|
||
|
PROP_ALLOW_CHILDREN,
|
||
|
PROP_NUM_PROPERTIES
|
||
|
};
|
||
|
|
||
|
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
|
||
|
|
||
|
static void
|
||
|
test_object_init (TestObject *obj)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_object_finalize (GObject *object)
|
||
|
{
|
||
|
TestObject *obj = TEST_OBJECT (object);
|
||
|
|
||
|
g_free (obj->string);
|
||
|
|
||
|
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_object_set_property (GObject *object,
|
||
|
guint property_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
TestObject *obj = TEST_OBJECT (object);
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_STRING:
|
||
|
g_free (obj->string);
|
||
|
obj->string = g_value_dup_string (value);
|
||
|
break;
|
||
|
|
||
|
case PROP_NUMBER:
|
||
|
obj->number = g_value_get_uint (value);
|
||
|
break;
|
||
|
|
||
|
case PROP_ALLOW_CHILDREN:
|
||
|
obj->allow_children = g_value_get_boolean (value);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_object_get_property (GObject *object,
|
||
|
guint property_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
TestObject *obj = TEST_OBJECT (object);
|
||
|
|
||
|
switch (property_id)
|
||
|
{
|
||
|
case PROP_STRING:
|
||
|
g_value_set_string (value, obj->string);
|
||
|
break;
|
||
|
|
||
|
case PROP_NUMBER:
|
||
|
g_value_set_uint (value, obj->number);
|
||
|
break;
|
||
|
|
||
|
case PROP_ALLOW_CHILDREN:
|
||
|
g_value_set_boolean (value, obj->allow_children);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
test_object_class_init (TestObjectClass *class)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
|
|
||
|
object_class->finalize = test_object_finalize;
|
||
|
object_class->set_property = test_object_set_property;
|
||
|
object_class->get_property = test_object_get_property;
|
||
|
|
||
|
g_object_class_install_property (object_class, PROP_STRING,
|
||
|
g_param_spec_string ("string", "String", "String",
|
||
|
NULL,
|
||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
g_object_class_install_property (object_class, PROP_NUMBER,
|
||
|
g_param_spec_uint ("number", "Number", "Number",
|
||
|
0, G_MAXUINT, 0,
|
||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
|
||
|
g_object_class_install_property (object_class, PROP_ALLOW_CHILDREN,
|
||
|
g_param_spec_boolean ("allow-children", "Allow children", "Allow children",
|
||
|
FALSE,
|
||
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||
|
}
|
||
|
|
||
|
static TestObject *
|
||
|
test_object_new (const char *string,
|
||
|
guint number,
|
||
|
gboolean allow_children)
|
||
|
{
|
||
|
return g_object_new (TEST_TYPE_OBJECT,
|
||
|
"string", string,
|
||
|
"number", number,
|
||
|
"allow-children", allow_children,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
static const char *
|
||
|
test_object_get_string (TestObject *obj)
|
||
|
{
|
||
|
return obj->string;
|
||
|
}
|
||
|
|
||
|
static guint
|
||
|
test_object_get_number (TestObject *obj)
|
||
|
{
|
||
|
return obj->number;
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
test_object_get_allow_children (TestObject *obj)
|
||
|
{
|
||
|
return obj->allow_children;
|
||
|
}
|
||
|
|
||
|
/* * * */
|
||
|
|
||
|
static GListModel *
|
||
|
create_model (guint base,
|
||
|
guint n,
|
||
|
guint increment,
|
||
|
gboolean allow_children)
|
||
|
{
|
||
|
GListStore *store;
|
||
|
guint i;
|
||
|
|
||
|
store = g_list_store_new (TEST_TYPE_OBJECT);
|
||
|
for (i = 0; i < n; i++)
|
||
|
{
|
||
|
char *string;
|
||
|
guint number;
|
||
|
TestObject *obj;
|
||
|
|
||
|
number = base + i * increment;
|
||
|
string = g_strdup_printf ("%u", number);
|
||
|
obj = test_object_new (string, number, allow_children);
|
||
|
g_list_store_append (store, obj);
|
||
|
g_object_unref (obj);
|
||
|
g_free (string);
|
||
|
}
|
||
|
|
||
|
return G_LIST_MODEL (store);
|
||
|
}
|
||
|
|
||
|
static GListModel *
|
||
|
create_child_model (gpointer item,
|
||
|
gpointer user_data)
|
||
|
{
|
||
|
guint size = GPOINTER_TO_UINT (user_data);
|
||
|
guint base = test_object_get_number (TEST_OBJECT (item));
|
||
|
|
||
|
if (test_object_get_allow_children (TEST_OBJECT (item)))
|
||
|
return create_model (base, size, 1, FALSE);
|
||
|
else
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static GListModel *
|
||
|
create_tree_model (guint n, guint m)
|
||
|
{
|
||
|
return G_LIST_MODEL (gtk_tree_list_model_new (create_model (0, n, m, TRUE),
|
||
|
FALSE,
|
||
|
FALSE,
|
||
|
create_child_model,
|
||
|
GUINT_TO_POINTER (m), NULL));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
setup_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
entry = gtk_editable_label_new ("");
|
||
|
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 3);
|
||
|
gtk_list_item_set_child (item, entry);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
text_changed (GObject *object,
|
||
|
GParamSpec *pspec,
|
||
|
gpointer data)
|
||
|
{
|
||
|
const char *text;
|
||
|
|
||
|
text = gtk_editable_get_text (GTK_EDITABLE (object));
|
||
|
g_print ("text changed to '%s'\n", text);
|
||
|
g_object_set (data, "string", text, NULL);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
bind_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
TestObject *obj;
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
obj = gtk_list_item_get_item (item);
|
||
|
entry = gtk_list_item_get_child (item);
|
||
|
gtk_editable_set_text (GTK_EDITABLE (entry), test_object_get_string (obj));
|
||
|
g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), obj);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
unbind_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
TestObject *obj;
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
obj = gtk_list_item_get_item (item);
|
||
|
entry = gtk_list_item_get_child (item);
|
||
|
g_signal_handlers_disconnect_by_func (entry, text_changed, obj);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
setup_tree_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
GtkWidget *expander;
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
entry = gtk_editable_label_new ("");
|
||
|
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 3);
|
||
|
expander = gtk_tree_expander_new ();
|
||
|
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (expander), entry);
|
||
|
gtk_list_item_set_child (item, expander);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
bind_tree_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
GtkTreeListRow *row;
|
||
|
GtkTreeExpander *expander;
|
||
|
TestObject *obj;
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
row = gtk_list_item_get_item (item);
|
||
|
expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
|
||
|
gtk_tree_expander_set_list_row (expander, row);
|
||
|
obj = gtk_tree_list_row_get_item (row);
|
||
|
entry = gtk_tree_expander_get_child (expander);
|
||
|
gtk_editable_set_text (GTK_EDITABLE (entry), test_object_get_string (obj));
|
||
|
|
||
|
g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), obj);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
unbind_tree_item (GtkSignalListItemFactory *factory,
|
||
|
GtkListItem *item)
|
||
|
{
|
||
|
GtkTreeListRow *row;
|
||
|
GtkTreeExpander *expander;
|
||
|
TestObject *obj;
|
||
|
GtkWidget *entry;
|
||
|
|
||
|
row = gtk_list_item_get_item (item);
|
||
|
expander = GTK_TREE_EXPANDER (gtk_list_item_get_child (item));
|
||
|
obj = gtk_tree_list_row_get_item (row);
|
||
|
entry = gtk_tree_expander_get_child (expander);
|
||
|
g_signal_handlers_disconnect_by_func (entry, text_changed, obj);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window;
|
||
|
GtkWidget *box;
|
||
|
GtkWidget *label;
|
||
|
GtkWidget *box2;
|
||
|
GtkWidget *stack;
|
||
|
GtkWidget *switcher;
|
||
|
GtkWidget *sw;
|
||
|
GtkWidget *grid;
|
||
|
GtkWidget *list;
|
||
|
GtkWidget *cv;
|
||
|
GListModel *model;
|
||
|
GtkListItemFactory *factory;
|
||
|
|
||
|
gtk_init ();
|
||
|
|
||
|
window = gtk_window_new ();
|
||
|
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||
|
|
||
|
box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
||
|
gtk_window_set_child (GTK_WINDOW (window), box2);
|
||
|
|
||
|
switcher = gtk_stack_switcher_new ();
|
||
|
gtk_widget_set_halign (GTK_WIDGET (switcher), GTK_ALIGN_CENTER);
|
||
|
gtk_widget_set_margin_top (GTK_WIDGET (switcher), 10);
|
||
|
gtk_widget_set_margin_bottom (GTK_WIDGET (switcher), 10);
|
||
|
gtk_box_append (GTK_BOX (box2), switcher);
|
||
|
|
||
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||
|
gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
|
||
|
gtk_box_append (GTK_BOX (box2), box);
|
||
|
|
||
|
label = gtk_editable_label_new ("Drag me");
|
||
|
gtk_box_append (GTK_BOX (box), label);
|
||
|
|
||
|
stack = gtk_stack_new ();
|
||
|
gtk_widget_set_vexpand (stack, TRUE);
|
||
|
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
|
||
|
gtk_box_append (GTK_BOX (box), stack);
|
||
|
|
||
|
/* grid */
|
||
|
sw = gtk_scrolled_window_new ();
|
||
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), sw, "grid", "GtkGridView");
|
||
|
|
||
|
model = create_model (0, 400, 1, FALSE);
|
||
|
factory = gtk_signal_list_item_factory_new ();
|
||
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||
|
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||
|
|
||
|
grid = gtk_grid_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (model)), factory);
|
||
|
gtk_grid_view_set_min_columns (GTK_GRID_VIEW (grid), 20);
|
||
|
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (grid), 20);
|
||
|
|
||
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), grid);
|
||
|
|
||
|
/* list */
|
||
|
sw = gtk_scrolled_window_new ();
|
||
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), sw, "list", "GtkListView");
|
||
|
|
||
|
list = gtk_list_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (create_model (0, 400, 1, FALSE))), NULL);
|
||
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
|
||
|
|
||
|
factory = gtk_signal_list_item_factory_new ();
|
||
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||
|
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||
|
|
||
|
gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory);
|
||
|
g_object_unref (factory);
|
||
|
|
||
|
/* columnview */
|
||
|
sw = gtk_scrolled_window_new ();
|
||
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), sw, "column", "GtkColumnView");
|
||
|
|
||
|
cv = gtk_column_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (create_model (0, 400, 1, FALSE))));
|
||
|
|
||
|
for (guint i = 0; i < 20; i++)
|
||
|
{
|
||
|
GtkColumnViewColumn *column;
|
||
|
char *title;
|
||
|
|
||
|
factory = gtk_signal_list_item_factory_new ();
|
||
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_item), NULL);
|
||
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||
|
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item), NULL);
|
||
|
|
||
|
title = g_strdup_printf ("Column %u", i);
|
||
|
column = gtk_column_view_column_new (title, factory);
|
||
|
gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);
|
||
|
g_object_unref (column);
|
||
|
g_free (title);
|
||
|
}
|
||
|
|
||
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), cv);
|
||
|
|
||
|
/* tree */
|
||
|
sw = gtk_scrolled_window_new ();
|
||
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), sw, "tree", "Tree");
|
||
|
|
||
|
list = gtk_list_view_new (GTK_SELECTION_MODEL (gtk_single_selection_new (create_tree_model (20, 20))), NULL);
|
||
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
|
||
|
|
||
|
factory = gtk_signal_list_item_factory_new ();
|
||
|
g_signal_connect (factory, "setup", G_CALLBACK (setup_tree_item), NULL);
|
||
|
g_signal_connect (factory, "bind", G_CALLBACK (bind_tree_item), NULL);
|
||
|
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_tree_item), NULL);
|
||
|
|
||
|
gtk_list_view_set_factory (GTK_LIST_VIEW (list), factory);
|
||
|
g_object_unref (factory);
|
||
|
|
||
|
gtk_window_present (GTK_WINDOW (window));
|
||
|
|
||
|
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||
|
g_main_context_iteration (NULL, TRUE);
|
||
|
|
||
|
gtk_window_destroy (GTK_WINDOW (window));
|
||
|
|
||
|
return 0;
|
||
|
}
|