Commit 3317340a authored by Philip Withnall's avatar Philip Withnall
parent b0962e78
This diff is collapsed.
#include <glib.h>
#include <gio/gio.h>
#include "sample-list.h"
/* This is left undefined to keep the example short, modalities
* from retrieving backing data will vary from one implementation
* to another
*/
static guint get_chart_position_from_item (SampleListItem *item);
/* Example alternative adapter implementation
*
* SampleLazyList is an adapter for the list store, which adapts an underlying
* data source to a GListModel of SampleListItem objects. In this example the
* source of the underlying data (self->model) is left unspecified, but it
* could be any presentation-agnostic data source, such as a database or
* a GListModel containing non-widget objects. */
#define SAMPLE_TYPE_LAZY_LIST (sample_lazy_list_get_type ())
G_DECLARE_FINAL_TYPE (
SampleLazyList, sample_lazy_list, SAMPLE, LAZY_LIST, GObject)
struct _SampleLazyList
{
GObject parent_instance;
/* cache, using a GSequence is adapted for really large models,
* but implementations may use another data type such as GPtrArray
* or GArray for smaller models to decrease memory overhead */
GSequence *artists;
/* The underlying data model. In theory it could have any type,
* but in practice the adapter would declare a specific type that it
* must have (for example, a connection object from an SQLite database),
* and use that. */
gpointer model;
};
static void sample_lazy_list_iface_init (GListModelInterface *iface);
G_DEFINE_TYPE_WITH_CODE (SampleLazyList,
sample_lazy_list,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
sample_lazy_list_iface_init));
static void
sample_lazy_list_finalize (GObject *obj)
{
g_sequence_free (SAMPLE_LAZY_LIST (obj)->artists);
G_OBJECT_CLASS (sample_lazy_list_parent_class)->finalize (obj);
}
static void
sample_lazy_list_class_init (SampleLazyListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sample_lazy_list_finalize;
}
static GType
sample_lazy_list_get_item_type (GListModel *list)
{
return SAMPLE_TYPE_LIST_ITEM;
}
static guint
sample_lazy_list_get_n_items (GListModel *list)
{
return 42; /* Arbitrary number, implementation-dependent */
}
static gint
sample_artist_item_find (gconstpointer p1,
gconstpointer p2,
gpointer user_data)
{
guint pos1 = get_chart_position_from_item ((SampleListItem *) p1);
guint pos2 = *(guint *) p2;
return ((pos1 < pos2) ? -1 : ((pos1 == pos2) ? 0 : 1));
}
static gint
sample_artist_item_compare (gconstpointer p1,
gconstpointer p2,
gpointer user_data)
{
guint pos1 = get_chart_position_from_item ((SampleListItem *) p1);
guint pos2 = get_chart_position_from_item ((SampleListItem *) p2);
return ((pos1 < pos2) ? -1 : ((pos1 == pos2) ? 0 : 1));
}
static void
sample_artist_item_unreffed (SampleListItem *item, SampleLazyList *self)
{
GSequenceIter *iter;
iter = g_sequence_lookup (self->artists, item, sample_artist_item_compare,
NULL);
g_assert (iter);
g_sequence_remove (iter);
}
/* Here, lookup the artist item from the self->artists sequence
* and return it if it was found.
*
* Otherwise, construct a new artist item, add it to self->artists and
* return it.
*
* If memory is constrained, let the created object inherit from
* GInitiallyUnowned, take a weak reference on it and return that instead.
*
* When the weak reference gets notified, remove it from self->artists
* in order to return a new object if required.
*/
static gpointer
get_from_cache (SampleLazyList *self, guint position)
{
GSequenceIter *iter;
SampleListItem *res;
iter = g_sequence_lookup (self->artists, &position, sample_artist_item_find,
NULL);
if (iter)
{
return g_sequence_get (iter);
}
res = sample_list_item_new ();
/* Implementations should populate the view here, using data from some
* underlying self->model. */
/* ListItem will inherit from GInitiallyUnowned. Implementations may either
* call g_object_ref_sink on the new list item or, as we do here, let List
* assume ownership of the list item, and set a weak reference to be notified
* when it is disposed.
*/
g_sequence_insert_sorted (self->artists, res, sample_artist_item_compare,
NULL);
g_object_weak_ref (G_OBJECT (res), (GWeakNotify) sample_artist_item_unreffed,
self);
return res;
}
static gpointer
sample_lazy_list_get_item (GListModel *self, guint position)
{
return get_from_cache (SAMPLE_LAZY_LIST (self), position);
}
static void
sample_lazy_list_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sample_lazy_list_get_item_type;
iface->get_n_items = sample_lazy_list_get_n_items;
iface->get_item = sample_lazy_list_get_item;
}
static void
sample_lazy_list_init (SampleLazyList *self)
{
/* Passing NULL to the data_destroy function, as in this example
* we do not keep full references on them */
self->artists = g_sequence_new (NULL);
}
#include "sample-list.h"
/* In this example, our backing model is a simple GList */
typedef GList BackingModel;
static void
backing_model_free (BackingModel *model)
{
g_list_free_full (model, g_object_unref);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (BackingModel, backing_model_free)
static GList *
create_backing_model (void)
{
GList *res = NULL;
res = g_list_prepend (res, sample_artist_new ("GYBE"));
return res;
}
int
main (int ac, char **av)
{
SampleList *list = sample_list_new ();
g_autoptr (BackingModel) backing_model = create_backing_model ();
g_autoptr (SampleListAdapter) adapter;
/* Create a generic adapter and pass it to the list widget.
* In our case, the adapter will automatically create list
* items containing a single ClutterText, representing the
* name of the artist. The adapter essentially converts a GList of
* SampleArtist objects to a GListModel of ClutterText objects.
*/
adapter = sample_list_adapter_new_from_g_list (backing_model);
sample_list_set_adapter (list, G_LIST_MODEL (adapter));
/* We may now expose the list to the user, once that is done
* we can drop our reference to the list. This will release the
* last remaining reference to the adapter. */
g_object_unref (list);
/* All references are released */
return 0;
}
#include "sample-list.h"
/* In this example, our backing model is a simple GPtrArray */
typedef struct
{
gchar *name;
} SampleArtist;
static void
free_sample_artist (SampleArtist *artist)
{
g_free (artist->name);
g_free (artist);
}
static SampleArtist *
create_sample_artist (const gchar *name)
{
SampleArtist *res = g_new0 (SampleArtist, 1);
res->name = g_strdup (name);
return res;
}
static GPtrArray *
create_sample_artists (void)
{
GPtrArray *res;
res = g_ptr_array_new_with_free_func ((GDestroyNotify) free_sample_artist);
g_ptr_array_add (res, create_sample_artist ("GYBE"));
return res;
}
static void
create_sample_artist_item (SampleArtist *artist, GListStore *adapter)
{
SampleListItem *item = sample_list_item_new ();
ClutterActor *name_label =
clutter_text_new_with_text (artist->name, "Sans 12");
clutter_actor_add_child (CLUTTER_ACTOR (item), name_label);
/* The list store takes ownership of the new artist item so we do not need to
* unref it.
*/
g_list_store_append (adapter, item);
}
SampleList *
create_sample_list (void)
{
g_autoptr (GListStore) adapter;
g_autoptr (SampleList) list;
g_autoptr (GPtrArray) backend_artists = create_sample_artists ();
adapter = g_list_store_new (SAMPLE_TYPE_LIST_ITEM);
/* Populate the adapter with artist views */
g_ptr_array_foreach (backend_artists, (GFunc) create_sample_artist_item,
adapter);
/* Create the list widget and pass the adapter */
list = sample_list_new ();
/* This takes a reference to adapter */
sample_list_set_adapter (list, G_LIST_MODEL (adapter));
return g_steal_pointer (&list);
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
/* We may now expose the list to the user, once that is done
* we can drop our reference to the list. This will release the
* last remaining reference to the adapter, which will in turn
* release the last references to individual artist items.
*/
g_object_unref (list);
/* All references are released */
return 0;
}
#include "sample-utils.h"
static gboolean
filter_artist (guint index)
{
/* Here we could retrieve the artist at the specified index
* from our backing data model, and return TRUE or FALSE
* based on its attributes. For simplicity we only make it
* so the first 20 items get displayed */
return (index <= 20);
}
static void
filter_artists (GListStore *store)
{
guint i, n_items;
n_items = g_list_model_get_n_items (G_LIST_MODEL (store));
for (i = 0; i < n_items; i++)
{
if (!filter_artist (i))
g_list_store_remove (store, i);
}
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
filter_artists (G_LIST_STORE (sample_list_get_adapter (list)));
/* now display to the list to the user, then ... */
g_object_unref (list);
return 0;
}
#include "sample-list.h"
static SampleListItem *
create_sample_artist_item (const gchar *name)
{
SampleListItem *res = sample_list_item_new ();
ClutterActor *name_label = clutter_text_new_with_text (name, "Sans 12");
clutter_actor_add_child (CLUTTER_ACTOR (res), name_label);
return res;
}
static SampleListItem *
create_sample_header_item (const gchar *header_text)
{
SampleListItem *res = sample_list_item_new ();
ClutterActor *name_label =
clutter_text_new_with_text (header_text, "Sans 12");
clutter_actor_add_child (CLUTTER_ACTOR (res), name_label);
g_object_set (res, "selectable", FALSE, NULL);
return res;
}
static SampleList *
create_sample_list (void)
{
GListStore *adapter;
SampleList *list;
adapter = g_list_store_new (SAMPLE_TYPE_LIST_ITEM);
g_list_store_append (adapter, create_sample_header_item ("A"));
g_list_store_append (adapter, create_sample_artist_item ("ABBA"));
g_list_store_append (adapter, create_sample_header_item ("B"));
g_list_store_append (adapter, create_sample_artist_item ("Bob Marley"));
list = sample_list_new ();
sample_list_set_adapter (list, G_LIST_MODEL (adapter));
g_object_unref (adapter);
return list;
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
/* We may now expose the list to the user, once that is done ... */
g_object_unref (list);
return 0;
}
#include "sample-utils.h"
static void
list_item_activated_cb (SampleList *list,
SampleListItem *item,
gpointer user_data)
{
/* Update the list item to show more information about the artist */
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
g_signal_connect (list, "item-activated",
G_CALLBACK (list_item_activated_cb), NULL);
/* Now display the list to the user, then ... */
g_object_unref (list);
return 0;
}
#include "sample-utils.h"
static void
item_created_cb (SampleList *list, SampleListItem *item)
{
if (sample_list_item_get_index (item) % 2)
sample_list_item_set_is_selectable (item, TRUE);
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
g_signal_connect (list, "item-created", G_CALLBACK (item_created_cb), NULL);
/* now display to the list to the user, then ... */
g_object_unref (list);
return 0;
}
#include <gdk/gdk.h>
#include <cogl/cogl.h>
#include "sample-utils.h"
static ClutterContent *
create_empty_image (void)
{
ClutterContent *res;
static guint8 empty[] = { 0, 0, 0, 0xff };
res = clutter_image_new ();
/* Here, we simply set a black background as a placeholder,
* other implementations may use an actual image.
*/
clutter_image_set_data (CLUTTER_IMAGE (res), empty,
COGL_PIXEL_FORMAT_RGBA_8888, 1, 1, 1, NULL);
return res;
}
static void
_pixbuf_loaded_cb (GObject *source, GAsyncResult *result, gpointer user_data)
{
g_autoptr (GdkPixbuf) pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_stream_finish (result, &error);
if (pixbuf)
clutter_image_set_data (
CLUTTER_IMAGE (user_data), gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888
: COGL_PIXEL_FORMAT_RGB_888,
gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf), NULL);
g_object_unref (user_data);
}
static void
list_item_showing_cb (SampleListItem *item,
GParamSpec *pspec,
gpointer user_data)
{
ClutterContent *image;
GError *error = NULL;
g_autoptr (GFile) file;
g_autoptr (GInputStream) stream;
/* We already loaded the image for this item */
if (clutter_actor_get_content (CLUTTER_ACTOR (item)) != NULL)
return;
file = g_file_new_for_path ("file:///some/path");
stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
if (!stream)
return;
/* We only load the image once it needs to be shown to the user */
image = create_empty_image ();
/* We take a reference to the image, to make sure it does not get
* disposed before loading is done
*/
gdk_pixbuf_new_from_stream_async (stream, NULL,
(GAsyncReadyCallback) _pixbuf_loaded_cb,
g_object_ref (image));
/* For simplicity, we set the image as the content of the item,
* more advanced usage would have us set it as the content of
* a child actor */
clutter_actor_set_content (CLUTTER_ACTOR (item), image);
}
static void
list_item_created_cb (SampleList *list,
SampleListItem *item,
gpointer user_data)
{
g_signal_connect (item, "notify::showing", (GCallback) list_item_showing_cb,
NULL);
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
/* Connect to the notify::showing signal from every list item. */
g_signal_connect (list, "item-created", (GCallback) list_item_created_cb,
NULL);
/* now display to the list to the user, then ... */
g_object_unref (list);
return 0;
}
#include "sample-utils.h"
static void
selected_items_changed_cb (SampleList *list)
{
GArray *selected;
selected = sample_list_get_selected_items (list);
/* Now do something with the items */
g_array_free (selected, TRUE);
}
int
main (int ac, char **av)
{
SampleList *list = create_sample_list ();
g_object_set (list, "selection-mode", SAMPLE_SELECTION_MULTIPLE, NULL);
g_signal_connect (list, "selected-items-changed",
G_CALLBACK (selected_items_changed_cb), NULL);
/* now display to the list to the user, then ... */
g_object_unref (list);
return 0;
}
#ifndef SAMPLE_UTILS_H_
#define SAMPLE_UTILS_H_
#include "sample-list.h"
SampleList *create_sample_list (void);
#endif /* SAMPLE_UTILS_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -12,6 +12,7 @@ index.md
global-search.md
internationalization.md
inter-domain-communication.md
list.md
media-management.md
multimedia.md
multiuser.md
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment