From b18c938b143b48f1903420c15b0dccf9dc0a7f32 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 18 Jun 2013 16:09:55 +0200 Subject: [PATCH] gtk-demo: Add GtkListBox demo --- demos/gtk-demo/Makefile.am | 3 + demos/gtk-demo/demo.gresource.xml | 7 + demos/gtk-demo/listbox.c | 376 ++++++++++++++++++++++++++ demos/gtk-demo/listbox.ui | 425 ++++++++++++++++++++++++++++++ demos/gtk-demo/messages.txt | 8 + 5 files changed, 819 insertions(+) create mode 100644 demos/gtk-demo/listbox.c create mode 100644 demos/gtk-demo/listbox.ui create mode 100644 demos/gtk-demo/messages.txt diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 8e8da27afe..fa74321e22 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -30,6 +30,7 @@ demos = \ images.c \ infobar.c \ links.c \ + listbox.c \ list_store.c \ menus.c \ offscreen_window.c \ @@ -116,6 +117,7 @@ RESOURCES= $(demos) \ stack.ui \ revealer.ui \ theming.ui \ + listbox.ui \ alphatest.png \ apple-red.png \ background.jpg \ @@ -132,6 +134,7 @@ RESOURCES= $(demos) \ gtk-logo-24.png \ gtk-logo-48.png \ gtk-logo-old.png \ + messages.txt \ css_accordion.css \ css_basics.css \ css_multiplebgs.css \ diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index b95fcf9304..f281a59b1e 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -104,6 +104,7 @@ images.c infobar.c links.c + listbox.c list_store.c menus.c offscreen_window.c @@ -133,4 +134,10 @@ floppybuddy.gif gtk-logo-old.png + + listbox.ui + messages.txt + gtk-logo-48.png + apple-red.png + diff --git a/demos/gtk-demo/listbox.c b/demos/gtk-demo/listbox.c new file mode 100644 index 0000000000..94ef2f1f3c --- /dev/null +++ b/demos/gtk-demo/listbox.c @@ -0,0 +1,376 @@ +/* ListBox + * + * GtkListBox allows lists with complicated layouts, using + * regular widgets supporting sorting and filtering. + * + */ + +#include +#include +#include + +static GdkPixbuf *avatar_pixbuf_gtk; +static GdkPixbuf *avatar_pixbuf_other; +static GtkWidget *window = NULL; + +#define GTK_TYPE_MESSAGE (gtk_message_get_type ()) +#define GTK_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_CAST ((message), GTK_TYPE_MESSAGE, GtkMessage)) +#define GTK_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MESSAGE, GtkMessageClass)) +#define GTK_IS_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_TYPE ((message), GTK_TYPE_MESSAGE)) +#define GTK_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MESSAGE)) +#define GTK_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MESSAGE, GtkMessageClass)) + +#define GTK_TYPE_MESSAGE_ROW (gtk_message_row_get_type ()) +#define GTK_MESSAGE_ROW(message_row) (G_TYPE_CHECK_INSTANCE_CAST ((message_row), GTK_TYPE_MESSAGE_ROW, GtkMessageRow)) +#define GTK_MESSAGE_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MESSAGE_ROW, GtkMessageRowClass)) +#define GTK_IS_MESSAGE_ROW(message_row) (G_TYPE_CHECK_INSTANCE_TYPE ((message_row), GTK_TYPE_MESSAGE_ROW)) +#define GTK_IS_MESSAGE_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MESSAGE_ROW)) +#define GTK_MESSAGE_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MESSAGE_ROW, GtkMessageRowClass)) + +typedef struct _GtkMessage GtkMessage; +typedef struct _GtkMessageClass GtkMessageClass; +typedef struct _GtkMessageRow GtkMessageRow; +typedef struct _GtkMessageRowClass GtkMessageRowClass; +typedef struct _GtkMessageRowPrivate GtkMessageRowPrivate; + + +struct _GtkMessage +{ + GObject parent; + + guint id; + char *sender_name; + char *sender_nick; + char *message; + gint64 time; + guint reply_to; + char *resent_by; + int n_favorites; + int n_reshares; +}; + +struct _GtkMessageClass +{ + GObjectClass parent_class; +}; + +struct _GtkMessageRow +{ + GtkListBoxRow parent; + + GtkMessageRowPrivate *priv; +}; + +struct _GtkMessageRowClass +{ + GtkListBoxRowClass parent_class; +}; + +struct _GtkMessageRowPrivate +{ + GtkMessage *message; + GtkRevealer *details_revealer; + GtkImage *avatar_image; + GtkWidget *extra_buttons_box; + GtkLabel *content_label; + GtkLabel *source_name; + GtkLabel *source_nick; + GtkLabel *short_time_label; + GtkLabel *detailed_time_label; + GtkBox *resent_box; + GtkLinkButton *resent_by_button; + GtkLabel *n_favorites_label; + GtkLabel *n_reshares_label; + GtkButton *expand_button; +}; + +GType gtk_message_get_type (void) G_GNUC_CONST; +GType gtk_message_row_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (GtkMessage, gtk_message, G_TYPE_OBJECT); + +static void +gtk_message_class_init (GtkMessageClass *klass) +{ +} + +static void +gtk_message_init (GtkMessage *msg) +{ +} + +static void +gtk_message_parse (GtkMessage *msg, const char *str) +{ + char **strv; + int i; + + strv = g_strsplit (str, "|", 0); + + i = 0; + msg->id = strtol (strv[i++], NULL, 10); + msg->sender_name = g_strdup (strv[i++]); + msg->sender_nick = g_strdup (strv[i++]); + msg->message = g_strdup (strv[i++]); + msg->time = strtol (strv[i++], NULL, 10); + if (strv[i]) + { + msg->reply_to = strtol (strv[i++], NULL, 10); + if (strv[i]) + { + if (*strv[i]) + msg->resent_by = g_strdup (strv[i]); + i++; + if (strv[i]) + { + msg->n_favorites = strtol (strv[i++], NULL, 10); + if (strv[i]) + { + msg->n_reshares = strtol (strv[i++], NULL, 10); + } + + } + } + } + + g_strfreev (strv); +} + +static GtkMessage * +gtk_message_new (const char *str) +{ + GtkMessage *msg; + msg = g_object_new (gtk_message_get_type (), NULL); + gtk_message_parse (msg, str); + return msg; +} + +G_DEFINE_TYPE (GtkMessageRow, gtk_message_row, GTK_TYPE_LIST_BOX_ROW); + + +static void +gtk_message_row_update (GtkMessageRow *row) +{ + GtkMessageRowPrivate *priv = row->priv; + GDateTime *t; + char *s; + + gtk_label_set_text (priv->source_name, priv->message->sender_name); + gtk_label_set_text (priv->source_nick, priv->message->sender_nick); + gtk_label_set_text (priv->content_label, priv->message->message); + t = g_date_time_new_from_unix_utc (priv->message->time); + s = g_date_time_format (t, "%e %b %y"); + gtk_label_set_text (priv->short_time_label, s); + g_free (s); + s = g_date_time_format (t, "%X - %e %b %Y"); + gtk_label_set_text (priv->detailed_time_label, s); + g_free (s); + + gtk_widget_set_visible (GTK_WIDGET(priv->n_favorites_label), + priv->message->n_favorites != 0); + s = g_strdup_printf ("%d\nFavorites", priv->message->n_favorites); + gtk_label_set_markup (priv->n_favorites_label, s); + g_free (s); + + gtk_widget_set_visible (GTK_WIDGET(priv->n_reshares_label), + priv->message->n_reshares != 0); + s = g_strdup_printf ("%d\nReshares", priv->message->n_reshares); + gtk_label_set_markup (priv->n_reshares_label, s); + g_free (s); + + gtk_widget_set_visible (GTK_WIDGET (priv->resent_box), priv->message->resent_by != NULL); + if (priv->message->resent_by) + gtk_button_set_label (GTK_BUTTON (priv->resent_by_button), priv->message->resent_by); + + if (strcmp (priv->message->sender_nick, "@GTKtoolkit") == 0) + gtk_image_set_from_pixbuf (priv->avatar_image, avatar_pixbuf_gtk); + else + gtk_image_set_from_pixbuf (priv->avatar_image, avatar_pixbuf_other); + +} + +static void +gtk_message_row_expand (GtkMessageRow *row) +{ + GtkMessageRowPrivate *priv = row->priv; + gboolean expand; + + expand = !gtk_revealer_get_reveal_child (priv->details_revealer); + + gtk_revealer_set_reveal_child (priv->details_revealer, expand); + if (expand) + gtk_button_set_label (priv->expand_button, "Hide"); + else + gtk_button_set_label (priv->expand_button, "Expand"); +} + +static void +expand_clicked (GtkMessageRow *row, + GtkButton *button) +{ + gtk_message_row_expand (row); +} + +static void +reshare_clicked (GtkMessageRow *row, + GtkButton *button) +{ + GtkMessageRowPrivate *priv = row->priv; + + priv->message->n_reshares++; + gtk_message_row_update (row); + +} + +static void +favorite_clicked (GtkMessageRow *row, + GtkButton *button) +{ + GtkMessageRowPrivate *priv = row->priv; + + priv->message->n_favorites++; + gtk_message_row_update (row); +} + +static void +gtk_message_row_state_flags_changed (GtkWidget *widget, + GtkStateFlags previous_state_flags) +{ + GtkMessageRowPrivate *priv = GTK_MESSAGE_ROW (widget)->priv; + GtkStateFlags flags; + + flags = gtk_widget_get_state_flags (widget); + + gtk_widget_set_visible (priv->extra_buttons_box, + flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED)); + + GTK_WIDGET_CLASS (gtk_message_row_parent_class)->state_flags_changed (widget, previous_state_flags); +} + +static void +gtk_message_row_class_init (GtkMessageRowClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GtkMessageRowPrivate)); + + gtk_widget_class_set_template_from_resource (widget_class, "/listbox/listbox.ui"); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, content_label); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, source_name); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, source_nick); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, short_time_label); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, detailed_time_label); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, extra_buttons_box); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, details_revealer); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, avatar_image); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, resent_box); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, resent_by_button); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, n_reshares_label); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, n_favorites_label); + gtk_widget_class_bind_child(widget_class, GtkMessageRowPrivate, expand_button); + gtk_widget_class_bind_callback (widget_class, expand_clicked); + gtk_widget_class_bind_callback (widget_class, reshare_clicked); + gtk_widget_class_bind_callback (widget_class, favorite_clicked); + + widget_class->state_flags_changed = gtk_message_row_state_flags_changed; +} + +static void +gtk_message_row_init (GtkMessageRow *row) +{ + GtkMessageRowPrivate *priv; + + row->priv = priv = + G_TYPE_INSTANCE_GET_PRIVATE (row, GTK_TYPE_MESSAGE_ROW, GtkMessageRowPrivate); + + gtk_widget_init_template (GTK_WIDGET (row)); +} + +static GtkMessageRow * +gtk_message_row_new (GtkMessage *message) +{ + GtkMessageRow *row; + + row = g_object_new (gtk_message_row_get_type (), NULL); + row->priv->message = message; + gtk_message_row_update (row); + + return row; +} + +static int +gtk_message_row_sort (GtkMessageRow *a, GtkMessageRow *b, gpointer data) +{ + return a->priv->message->time - b->priv->message->time; +} + +static void +row_activated (GtkListBox *listbox, GtkListBoxRow *row) +{ + gtk_message_row_expand (GTK_MESSAGE_ROW (row)); +} + +GtkWidget * +do_listbox (GtkWidget *do_widget) +{ + GtkWidget *scrolled, *listbox, *vbox, *label; + GtkMessage *message; + GtkMessageRow *row; + GBytes *data; + char **lines; + int i; + + if (!window) + { + avatar_pixbuf_gtk = gdk_pixbuf_new_from_resource_at_scale ("/listbox/gtk-logo-48.png", 32, 32, FALSE, NULL); + avatar_pixbuf_other = gdk_pixbuf_new_from_resource_at_scale ("/listbox/apple-red.png", 32, 32, FALSE, NULL); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_screen (GTK_WINDOW (window), + gtk_widget_get_screen (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "ListBox"); + gtk_window_set_default_size (GTK_WINDOW (window), + 400, 600); + + /* NULL window variable when window is closed */ + g_signal_connect (window, "destroy", + G_CALLBACK (gtk_widget_destroyed), + &window); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_add (GTK_CONTAINER (window), vbox); + label = gtk_label_new ("Messages from Gtk+ and friends"); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0); + listbox = gtk_list_box_new (); + gtk_container_add (GTK_CONTAINER (scrolled), listbox); + + gtk_list_box_set_sort_func (GTK_LIST_BOX (listbox), (GtkListBoxSortFunc)gtk_message_row_sort, listbox, NULL); + gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (listbox), FALSE); + g_signal_connect (listbox, "row-activated", G_CALLBACK (row_activated), NULL); + + gtk_widget_show_all (vbox); + + data = g_resources_lookup_data ("/listbox/messages.txt", 0, NULL); + lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0); + + for (i = 0; lines[i] != NULL && *lines[i]; i++) + { + message = gtk_message_new (lines[i]); + row = gtk_message_row_new (message); + gtk_widget_show (GTK_WIDGET (row)); + gtk_container_add (GTK_CONTAINER (listbox), GTK_WIDGET (row)); + } + + g_strfreev (lines); + } + + if (!gtk_widget_get_visible (window)) + gtk_widget_show (window); + else + gtk_widget_destroy (window); + + return window; +} diff --git a/demos/gtk-demo/listbox.ui b/demos/gtk-demo/listbox.ui new file mode 100644 index 0000000000..f2d8fca391 --- /dev/null +++ b/demos/gtk-demo/listbox.ui @@ -0,0 +1,425 @@ + + + + + + True + False + + + True + False + Email message + True + + + + + True + False + Embed message + True + + + + + diff --git a/demos/gtk-demo/messages.txt b/demos/gtk-demo/messages.txt new file mode 100644 index 0000000000..e681f30e23 --- /dev/null +++ b/demos/gtk-demo/messages.txt @@ -0,0 +1,8 @@ +1|GTK+ and friends|@GTKtoolkit|GTK+ 3.8.0 (STABLE) released: wayland, Multi-application Broadway, improved CSS support and more ... http://ur1.ca/d6yms #gtk #gtk3|1364338800|0||4|2 +2|Daniel Svensson|@dsvensson|Bringing an application up to the new features in GTK 3.x = tons of negative diffs, awesome work by @GTKtoolkit devs <3|1382565600|0|GTK+ and friends|0|1 +3|GTK+ and friends|@GTKtoolkit|GLib status update and a warning: http://ur1.ca/awsm1 #glib|1384383600 +4|GTK+ and friends|@GTKtoolkit|GProperty status: http://ur1.ca/awslh #glib|1384383300 +5|GTK+ and friends|@GTKtoolkit|GTK+ 3.6.2 (STABLE) available: http://ur1.ca/awsl2 #gtk #gtk3|1384383000 +6|GTK+ and friends|@GTKtoolkit|GLib 2.34.2 (STABLE) available: http://ur1.ca/awskn #glib|1384383000 +7|GTK+ and friends|@GTKtoolkit|GTK+ 3.6.0 (STABLE) released: http://ur1.ca/aj4e0 #gtk #gtk3|1381528800 +8|GTK+ and friends|@GTKtoolkit|GLib 2.34.0 (STABLE) released: http://ur1.ca/aj4du #glib|1381522800