0051: MVC IV – The ComboBox with Text
Today starts a mini-series within our MVC series in which we look at a simple ComboBox example to reproduce what we’ve already done with the ComboBoxText.
So… unlike the ComboBoxText—which you more or less just throw strings at—the ComboBox needs an actual Model (ListStore or TreeStore) to draw text strings from. This is a little more work than it was with the ComboBoxText, so let’s dig in…
The Model
We start with a ListStore class:
class SignListStore : ListStore
{
string[] items = ["bike", "bump", "cow", "deer", "crumbling cliff", "man with a stop sign", "skidding vehicle"];
TreeIter treeIter;
this()
{
super([GType.STRING]);
foreach(ulong i; 0..items.length)
{
string message = items[i];
treeIter = createIter();
setValue(treeIter, 0, message);
}
} // this()
} // class SignListStore
Now, there are similarities. We still have a string array, but how we handle it is quite different.
The Model Constructor
The ListStore constructor takes an array of GTypes and these define the data types each column in the ListStore will hold. The GType enum, found in generated/gtkd/gobject/c/types.d, defines all the built-in types we can use here. Later, when we look at more complex examples, we’ll go over how to deal with more complex types, but for now, these will do.
You’ll note that the call to super() still gets an array for an argument, even though we’re only using one data type. And, of course, because there’s only one element in the array, there will be only one column.
The foreach() loop steps through the array, picks one of the items, instantiates a TreeIter, and then sets the value in the ListStore row.
The setValue() arguments are:
treeIter– a pointer to theListStorerow where we’ll store the current data,0– the column number (in this case the only column we have) within theListStorewhere the data will end up, andmessage– the string data we’re storing.
The View/Control
The ComboBox acts as both View and Control. Keep in mind that it’s based on the CellLayout interface and so it a non-standard implementation of the MVC paradigm. But, no matter. The results are so similar, they make no real difference, so let’s carry on.
Let’s look at the SignComboBox a bit at a time starting with…
The Initialization Chunk
class SignComboBox : ComboBox
{
private:
bool entryOn = false;
SignListStore _signListStore;
CellRendererText cellRendererText;
int visibleColumn = 0;
int activeItem = 0;
Here’s what these are:
entryOnwe’ve used before and with it being false, it stops theComboBoxfrom including anEntrywidget,_signListStoreis just a convenient (and local) place to keep a pointer to theListStore,cellRendererTexttells theComboBoxthat we’ll be working with and displaying text items,visibleColumnis theListStorecolumn number from which we’ll draw data, andactiveItemis theListStorerow number (index) that’ll be selected by default.
We’ll talk more about the visibleColumn variable and CellRenderers of various types when we look at other examples later in this mini-series.
The Constructor
public:
this(SignListStore signListStore)
{
super(entryOn);
// set up the ComboBox's column to render text
cellRendererText = new CellRendererText();
packStart(cellRendererText, false);
addAttribute(cellRendererText, "text", visibleColumn);
// set up and bring in the store
_signListStore = signListStore;
setModel(_signListStore);
setActive(activeItem);
addOnChanged(&doSomething);
} // this()
After the instantiation of the super-class, we have three stages to this constructor:
- setting up and packing the
CellRenderer, - initializing the
ListStore(Model), and - hooking up the signal.
Stage 1: CellRendererText
In the introduction to this series, I mentioned that one or more CellRenderers are packed into a TreeViewColumn so it knows how to display its contents. With a ComboBox, we don’t have a TreeViewColumn. Instead, as I also said earlier, the ComboBox is an implementation of the CellLayout interface. This interface is also implemented by the TreeViewColumn which means the ComboBox acts as its own TreeViewColumn of a sort. From a practical point of view, all this means is that you can treat the ComboBox as if it has a TreeViewColumn… sort of. Later on, we’ll dig into this a bit and see how flexible this can be.
For now, though, this is what happens in the first stage of the constructor:
- the
CellRendereris instantiated, - its packed into the
ComboBox, and - we use
addAttribute()to tell theComboBox:- what its single column will display,
- which
CellRendererto use, and - which column will be visible (in this case, the only column we have).
Stage 2: Initializing the Model
Not a big deal, we just:
- assign a local pointer to the
ListStore, - use
setModel()to tell theComboBoxwhere to look for its data, and - pre-select one of the items, using
setActive(), so theComboBoxshows a default value.
Moving on…
Stage 3: The Callback
And the last line of the constructor hooks up the callback signal, but that’s straightforward, so let’s look at the callback code itself:
void doSomething(ComboBox cb)
{
string data;
TreeIter treeIter;
write("index of selection: ", getActive(), ", ");
if(getActiveIter(treeIter) == true)
{
data = getModel().getValueString(treeIter, 0);
writeln("data: ", data);
}
} // doSomething()
Again, we define a TreeIter which we’ll go over in a moment.
The first action we take is to get the index of the currently-selected item. This is here purely for completeness sake. It really has nothing to do with the next step…
which is where we use the TreeIter, not to stuff data into the ListStore, but to retrieve it. The getActiveIter() function returns a Boolean to indicate success or failure, so we can predicate further action on whether or not the TreeIter gets initialized here. And yes, it’s one of those D-language situations where the function definition looks like this:
public bool getActiveIter(out TreeIter iter)
And if you don’t yet know, that’s D’s way of asking a function to assign value to an argument. And to make things easy for this worker function, D has the ability to hand it the argument using the out qualifier.
Anyway, if the TreeIter gets instantiated by getActiveIter(), we then:
- use
getModel()to grab theListStore’sTreeModelso we can - use its
getValueString()function to grab the data stored in the first (0th) column of the Model.
It looks and sounds far more complex than it actually is. We could have done the same thing like this:
model = getModel();
data = model.getValueString(treeIter, 0);
But, whatever. From there, we can do whatever we want with the fetched data. In this case, we just echo it to the terminal.
Conclusion
Okay, so there we have a reproduction of the ComboBoxText using a ComboBox and—who’d-a thunk it—some text. Sure, it’s more work, but as we’ll see in the rest of this mini-series within a series, when we turn to non-string data, we need to know this stuff.
See you next time when we tackle a ComboBox with integers.
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 2025 Ron Tarrant