GTK4 关于 TreeView 的新可能性并不容易实现。因此,最好有一个介绍性示例。
几个小时后,在这个论坛的宝贵建议的帮助下,我成功地为 ListView 的实现创建了一个小测试示例。也许对其他人来说也会很有趣。
重要通知:
该程序的唯一目的是测试使用 GTK4、C 和 VFL 的 ListView 的一些方法。它可能包含错误,并且并不声称被视为“最佳实践”。
/*
* The sole purpose of this program is to test some approaches to using
* ListView with GTK4, C, and VFL.It may contain errors and does not
* claim to be considered "best practice".
*/
#include <gtk/gtk.h>
static GListModel* create_model()
{
GListStore *store;
store = g_list_store_new(GTK_TYPE_STRING_OBJECT);
g_list_store_append(store, gtk_string_object_new("Washington,D.C."));
g_list_store_append(store, gtk_string_object_new("London"));
g_list_store_append(store, gtk_string_object_new("Paris"));
g_list_store_append(store, gtk_string_object_new("Berlin"));
g_list_store_append(store, gtk_string_object_new("Rom"));
return G_LIST_MODEL (store);
}
/*
* Create a class derived from Widget that contains the controls
* and implements the Layout Manager.
*/
#define LISTVFL_TYPE_LAYOUT (listvfl_layout_get_type())
G_DECLARE_FINAL_TYPE (ListvflLayout, listvfl_layout, LISTVFL, LAYOUT,
GtkWidget)
struct _ListvflLayout
{
GtkWidget parent_instance;
GtkWidget *scrolledwindow;
GtkWidget *listview;
GtkWidget *label;
GtkWidget *btndelete;
GtkWidget *btnaz;
GtkWidget *btnza;
GtkWidget *btnadd;
GtkWidget *entry;
GtkSingleSelection *selection;
guint position;
};
G_DEFINE_TYPE (ListvflLayout, listvfl_layout, GTK_TYPE_WIDGET)
static void
setup_list_item_cb (GtkListItemFactory *factory, GtkListItem *list_item)
{
GtkWidget*label = gtk_label_new (NULL);
gtk_list_item_set_child (GTK_LIST_ITEM(list_item), label);
}
static void
bind_list_item_cb (GtkListItemFactory *factory, GtkListItem *list_item,
gpointer listvfllayout)
{
GtkWidget *label = gtk_list_item_get_child(list_item);
GtkStringObject *str = gtk_list_item_get_item(list_item);
const char *string = gtk_string_object_get_string(str);
gtk_label_set_text(GTK_LABEL(label), string);
if (gtk_list_item_get_selected(list_item))
{
ListvflLayout *widget = (ListvflLayout*)listvfllayout;
GtkWidget *label1 = GTK_WIDGET(widget->label);
gchar *format = "<span foreground='blue'
background='yellow'size='x-large'>\%s </span>";
gchar *markup;
markup = g_markup_printf_escaped (format,string);
gtk_label_set_markup (GTK_LABEL(label1),markup);
g_free (markup);
widget->position = gtk_list_item_get_position(list_item);
}
}
// not in use !!
static void
selection_changed(GObject *object, GParamSpec *pspec, GtkWidget
*listvfllayout)
{
// GtkListItem *list_item
=gtk_single_selection_get_selected_item(GTK_SINGLE_SELECTION(object));
// pos = gtk_single_selection_get_selected(GTK_SINGLE_SELECTION(object));
// const char *string =
gtk_string_object_get_string(GTK_STRING_OBJECT(list_item));
// ListvflLayout *widget = (ListvflLayout*)listvfllayout;
// gtk_label_set_label(GTK_LABEL(widget->label),string);
}
gint sort_dec(gconstpointer *a, gconstpointer *b, gpointer un_used)
{
const char *string1 =
gtk_string_object_get_string(GTK_STRING_OBJECT(a));
const char *string2 =
gtk_string_object_get_string(GTK_STRING_OBJECT(b));
return g_strcmp0 (string1, string2);
}
gint sort_asc(gconstpointer *a, gconstpointer *b, gpointer un_used)
{
const char *string1 = gtk_string_object_get_string(GTK_STRING_OBJECT(a));
const char *string2 =
gtk_string_object_get_string(GTK_STRING_OBJECT(b));
return g_strcmp0 (string2, string1);
}
static void sort_az(GtkWidget * btnaz, gpointer listvfllayout)
{
ListvflLayout *widget = LISTVFL_LAYOUT(listvfllayout);
GListModel *model = gtk_single_selection_get_model(widget->selection);
GListStore *store = G_LIST_STORE(model);
g_list_store_sort(store,(GCompareDataFunc)sort_dec,NULL);
}
static void sort_za(GtkWidget *btnza, gpointer listvfllayout)
{
ListvflLayout *widget = LISTVFL_LAYOUT(listvfllayout);
GListModel *model = gtk_single_selection_get_model(widget->selection);
GListStore *store = G_LIST_STORE(model);
g_list_store_sort(store,(GCompareDataFunc)sort_asc,NULL);
}
static void delete(GtkWidget *btndelete, gpointer listvfllayout)
{
ListvflLayout *widget = LISTVFL_LAYOUT(listvfllayout);
GListModel *store = gtk_single_selection_get_model(widget->selection);
if (widget->position == 0) gtk_label_set_label(GTK_LABEL(widget-
>label),"-empty-");
if(g_list_model_get_n_items(store))
g_list_store_remove(G_LIST_STORE(store), widget->position);
}
static void add(GtkWidget *btnadd, gpointer listvfllayout)
{
ListvflLayout *widget = LISTVFL_LAYOUT(listvfllayout);
GtkEntryBuffer *buffer;
const char *text;
buffer = gtk_entry_get_buffer(GTK_ENTRY(widget->entry));
text = gtk_entry_buffer_get_text(buffer);
if (strlen(text) > 0)
{
GListModel *store = gtk_single_selection_get_model(widget-
>selection);
g_list_store_append(G_LIST_STORE(store),
gtk_string_object_new(text));
}
gtk_entry_buffer_delete_text(buffer,0,-1);
}
static void
listvfl_layout_dispose (GObject *object)
{
ListvflLayout *self = LISTVFL_LAYOUT (object);
g_clear_pointer (&self->scrolledwindow, gtk_widget_unparent);
g_clear_pointer (&self->label, gtk_widget_unparent);
g_clear_pointer (&self->btndelete, gtk_widget_unparent);
g_clear_pointer (&self->btnaz, gtk_widget_unparent);
g_clear_pointer (&self->btnza, gtk_widget_unparent);
g_clear_pointer (&self->btnadd, gtk_widget_unparent);
g_clear_pointer (&self->entry, gtk_widget_unparent);
G_OBJECT_CLASS (listvfl_layout_parent_class)->dispose (object);
}
static void
listvfl_layout_class_init (ListvflLayoutClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS(class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
object_class->dispose = listvfl_layout_dispose;
// Layout manager
gtk_widget_class_set_layout_manager_type
(widget_class,GTK_TYPE_CONSTRAINT_LAYOUT);
}
// Creating the Conistraints for the Layout
static void
build_constraints (ListvflLayout *self, GtkConstraintLayout *manager)
{
const char *const vfl[] = {
"H:|-20-[label(110)]-60-[btndelete(110)]-(<=0)-|",
"H:|-20-[scrolledwindow(110)]-60-[btnaz(50)]-10-[btnza(50)]-(<=0)-|",
"H:|-20-[scrolledwindow(110)]-60-[entry(110)]-(<=0)-|",
"H:|-20-[scrolledwindow(110)]-60-[btnadd(110)]-(<=0)-|",
"V:|-20-[label(30)]-20-[scrolledwindow(250)]-(<=0)-|",
"V:|-20-[btndelete(30)]-10-[btnaz(20)]-10-[entry(30)]-10-[btnadd(30)]-(<=0)-|",
"V:|-20-[btndelete(30)]-10-[btnza(20)]-(<=0)-|",
};
GError *error = NULL;
gtk_constraint_layout_add_constraints_from_description (manager, vfl, G_N_ELEMENTS (vfl),
0,0,
&error,
"label", self->label,
"btndelete", self->btndelete,
"scrolledwindow", self->scrolledwindow,
"listview", self->listview,
"btnaz", self->btnaz,
"btnza", self->btnza,
"entry", self->entry,
"btnadd", self->btnadd,
NULL);
if (error != NULL)
{
g_printerr("VFL parsing Error: %s\n", error->message);
g_error_free(error);
}
}
//Initializing the class
static void
listvfl_layout_init (ListvflLayout *self)
{
GtkListItemFactory *factory;
GListModel *model;
self->position = 0;
GtkWidget *widget = GTK_WIDGET (self);
self->label = gtk_label_new(NULL);
gtk_widget_set_parent(self->label,widget);
self->btndelete = gtk_button_new_with_label("Delete");
gtk_widget_add_css_class(self->btndelete, "destructive-action");
gtk_widget_set_parent(self->btndelete,widget);
g_signal_connect(self->btndelete,"clicked",G_CALLBACK(delete),self);
self->listview = gtk_list_view_new(NULL,NULL);
model = create_model();
self->selection = gtk_single_selection_new(G_LIST_MODEL(model));
gtk_single_selection_set_autoselect(self->selection, TRUE);
gtk_list_view_set_model(GTK_LIST_VIEW(self-
>listview),GTK_SELECTION_MODEL(self->selection));
g_signal_connect (self->selection,"notify::selected",
G_CALLBACK(selection_changed),self);
factory = gtk_signal_list_item_factory_new();
g_signal_connect (factory, "setup", G_CALLBACK (setup_list_item_cb),
NULL);
g_signal_connect (factory, "bind",G_CALLBACK (bind_list_item_cb),self);
gtk_list_view_set_factory (GTK_LIST_VIEW (self->listview),factory);
self->scrolledwindow = gtk_scrolled_window_new();
gtk_widget_set_parent(self->scrolledwindow,widget);
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self-
>scrolledwindow),self->listview);
self->btnaz = gtk_button_new_with_label("a-z");
gtk_widget_set_parent(self->btnaz,widget);
g_signal_connect(self->btnaz,"clicked",G_CALLBACK(sort_az),self);
self->btnza = gtk_button_new_with_label("z-a");
gtk_widget_set_parent(self->btnza,widget);
g_signal_connect(self->btnza,"clicked",G_CALLBACK(sort_za),self);
self->entry = gtk_entry_new();
gtk_widget_set_parent(self->entry,widget);
gtk_widget_set_focus_child(GTK_WIDGET(self),self->entry);
g_signal_connect(self->entry,"activate",G_CALLBACK(add),self);
self->btnadd = gtk_button_new_with_label("Add");
gtk_widget_add_css_class(self->btnadd, "suggested-action");
gtk_widget_set_parent(self->btnadd,widget);
g_signal_connect(self->btnadd,"clicked",G_CALLBACK(add),self);
//Import and initialize the widget's layout manager
GtkLayoutManager *manager = gtk_widget_get_layout_manager (GTK_WIDGET
(self));
build_constraints(self, GTK_CONSTRAINT_LAYOUT (manager));
}
//From here, a applicationswindow is created and the class is added via a
box
static void
activate (GtkApplication *app, gpointer user_date)
{
GtkWidget *window;
GtkWidget *box, *listvfllayout;
window = gtk_window_new();
gtk_window_set_title (GTK_WINDOW (window), "ListView editable with
VFL");
gtk_widget_set_size_request (window, 200,400);
g_object_add_weak_pointer (G_OBJECT (window),(gpointer *)&window);
box = gtk_box_new(GTK_ORIENTATION_VERTICAL,12);
gtk_window_set_child(GTK_WINDOW (window), box);
listvfllayout = g_object_new(listvfl_layout_get_type(), NULL);
gtk_widget_set_hexpand(listvfllayout, TRUE);
gtk_widget_set_vexpand(listvfllayout, TRUE);
gtk_box_append (GTK_BOX (box), listvfllayout);
gtk_application_add_window(app, GTK_WINDOW(window));
gtk_window_present (GTK_WINDOW(window));
}
int
main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new
("holger.test.viewflf",G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}