0053: MVC VI - A ComboBox with Images
And here’s a link to the zip file containing images used in this example.
So, we’ve done a two-column ListStore
, how about one with four columns? And this time, let’s throw in a Pixbuf
… which BTW isn’t defined in the GType enum
.
The SignListStore Class
Here’s the initialization section:
class SignListStore : ListStore
{
string[] items = ["bike", "bump", "cow", "deer", "crumbling cliff", "man with a stop sign", "skidding vehicle"];
int[] signNumbers = [1, 2, 3, 4, 5, 6, 7];
string[] images = ["_images/bicycle.png",
"_images/bump.png",
"_images/cattle.png",
"_images/deer.png",
"_images/falling_rocks.png",
"_images/road_crew.png",
"_images/slippery_road.png"];
string[] descriptions = ["Bicycles present",
"Bump ahead",
"Cattle crossing",
"Deer crossing",
"Falling rocks ahead",
"Road crew ahead",
"Slippery when wet"];
enum Column
{
THEME_COLUMN = 0,
NUMBER_COLUMN = 1,
IMAGE_COLUMN = 2,
DESCRIPTION_COLUMN = 3
} // enum Column
TreeIter treeIter;
And the definitions for the four columns mean we’re working with:
items
: one-word text tags for each image,signNumbers
: numbers associated with each image,images
: relative paths and file names for each image, anddescriptions
: strings with more complete descriptions of each sign.
You’ll also notice there’s an enum
(Column
) and we’ll see in a moment how these values are used, in the constructors for both the SignListStore
and the SignComboBox
.
Making the Column enum
part of SignListStore
means that anywhere we can access the ListStore
, we’ll be able to access these static column values. And if you take a peek at the AppBox
class…
class AppBox : Box
{
SignComboBox signComboBox;
SignListStore signListStore;
this()
{
super(Orientation.VERTICAL, 10);
signListStore = new SignListStore();
signComboBox = new SignComboBox(signListStore);
packStart(signComboBox, false, false, 0);
} // this()
} // class AppBox
… you’ll see that a pointer to signListStore
is passed into the SignComboBox
constructor.
Now let’s look at the SignListStore
constructor:
this()
{
string item, imageName, description;
int number;
super([GType.STRING, GType.INT, Pixbuf.getType(), GType.STRING]);
foreach(ulong i; 0..items.length)
{
item = items[i];
number = signNumbers[i];
imageName = images[i];
description = descriptions[i];
treeIter = createIter();
setValue(treeIter, Column.THEME_COLUMN, item);
setValue(treeIter, Column.NUMBER_COLUMN, number);
setValue(treeIter, Column.IMAGE_COLUMN, new Pixbuf(imageName));
setValue(treeIter, Column.DESCRIPTION_COLUMN, description);
}
} // this()
The first thing I’ll point out is the array we’re passing to super()
. See that Pixbuf.getType()
argument? It needs a bit of explanation, so here goes…
Non-GTypes
Any visible GTK/GDK/Pango object class—such as Pixbuf
, Color
, RBGA
, PgFontDescription
, along with a whole raft of others—can be used as ListStore
column data types. Some—like Color
, RBGA
, and PgFontDescription
—can do no more than decorate other columns, but others—like the Pixbuf
—can be visible in the ComboBox
. And we get the correct type to pass to the ListStore
constructor by asking the data class—not an instantiation of the class, but the base class itself—what type it is, kind of a “who goes there” approach:
Pixbuf.getType()
And that does the job. Include that right in the array we pass to the super-class constructor and it’s accepted as just another GType
.
Note: Because getType()
is a function call, it can’t be read at compile time, so we can’t predefine the array like we can if the columns all hold standard GTypes
. The array has to be defined in the constructor at the very earliest, or written out as it’s passed to the super-class constructor as can be seen here:
super([GType.STRING, GType.INT, Pixbuf.getType(), GType.STRING]);
And the foreach()
loop that calls setValue()
can now stuff everything into the ListStore
:
foreach(ulong i; 0..items.length)
{
item = items[i];
number = signNumbers[i];
imageName = images[i];
description = descriptions[i];
treeIter = createIter();
setValue(treeIter, Column.THEME_COLUMN, item);
setValue(treeIter, Column.NUMBER_COLUMN, number);
setValue(treeIter, Column.IMAGE_COLUMN, new Pixbuf(imageName));
setValue(treeIter, Column.DESCRIPTION_COLUMN, description);
}
Two things of note here:
- I mentioned earlier that we’d see how the
Column
enum values are used and here’s the first of those uses, to identify which column is being filled for each pass through theforeach()
loop, and - the call to
setValue()
that handles thePixbuf
column makes a call to thePixbuf
constructor, passing along the file name grabbed from theimageName
array.
Note: When your ListStore
is to contain only string data, you can use set()
instead of setValue()
and thereby plug data into all the columns of a single row in one statement, but when using non-string data types, doing a setValue()
on columns one at a time is your only option.
The SignComboBox Class
Rather than reproduce the entire SignComboBox
class here, we’ll just look at the bits and pieces we haven’t seen before…
Because we want to show images instead of text, we need to declare a CellRendererPixbuf
in the initialization section of the SignComboBox
class:
CellRendererPixbuf cellRendererPixbuf;
And in the constructor we have the instantiation, the packing, and the adding of attributes:
cellRendererPixbuf = new CellRendererPixbuf();
packStart(cellRendererPixbuf, false);
addAttribute(cellRendererPixbuf, "pixbuf", _signListStore.Column.IMAGE_COLUMN);
You’ll note that whereas with the CellRendererText
we used “text”
as an attribute name, here we use “pixbuf”
for the CellRendererPixbuf
. Stands to reason, right?
Note: There is a bit of terminology confusion with the second argument to the addAttribute()
statement. In the addAttribute()
function definition, this argument is called an attribute. But when you look in the online reference or one of the books written about GTK, they’re called properties.
Anyway, if you’re curious about this type of attribute/property, go to the GTK Reference Manual online and search in the page for CellRenderer
. Click on any of them and you’ll find a Properties section not too far down the page.
As we encounter each type of CellRenderer
, we’ll cover which property/attribute is used with each one.
The other thing to note about the call to addAttribute()
is its last argument:
_signListStore.Column.IMAGE_COLUMN
This is the second (and final) use of the Column
enum and it’s why the Column enum
was made part of the SignListStore
class. A pointer to the store itself gets passed into the SignComboBox
constructor, so we have access to it here without any major fiddling around.
Bonus Example – A 3-Column ComboBox
The folder/directory of images is used with this example as well.
This post is running a bit long, but this example really won’t take much explanation, so here goes…
There are only two differences between this example and the last. One’s in the constructor and it’s merely how many CellRenderer
s we stuff into the SignComboBox
. It happens in these lines in the constructor:
cellRendererInt = new CellRendererText();
packStart(cellRendererInt, false);
addAttribute(cellRendererInt, "text", _signListStore.Column.NUMBER_COLUMN);
cellRendererText = new CellRendererText();
packStart(cellRendererText, false);
addAttribute(cellRendererText, "text", _signListStore.Column.THEME_COLUMN);
cellRendererPixbuf = new CellRendererPixbuf();
packStart(cellRendererPixbuf, false);
addAttribute(cellRendererPixbuf, "pixbuf", _signListStore.Column.IMAGE_COLUMN);
The ComboBox
is derived from the Bin class and so all we have to do is pack in three CellRenderer
s, making sure we use an appropriate attribute (in this case, “text”
for the first two columns and “pixbuf”
for the last) and make sure they’re pointing to columns with matching data.
The other difference is…
There’s no visibleColumn
variable because they’re all visible.
So remember:
- you can have as many columns visible in the
ComboBox
as you wish, and - multiple calls to
addAttribute()
will make this happen.
Conclusion
And that wraps up our look at ComboBox
es with images. Next time we’ll start looking at the TreeView
.
Comments? Questions? Observations?
Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.
- come on over to the D Language Forum and look for one of the gtkDcoding announcement posts,
- drop by the GtkD Forum,
- follow the link below to email me, or
- go to the gtkDcoding Facebook page.
You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.
© Copyright 2024 Ron Tarrant