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 theListStore
row where we’ll store the current data,0
– the column number (in this case the only column we have) within theListStore
where 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:
entryOn
we’ve used before and with it being false, it stops theComboBox
from including anEntry
widget,_signListStore
is just a convenient (and local) place to keep a pointer to theListStore
,cellRendererText
tells theComboBox
that we’ll be working with and displaying text items,visibleColumn
is theListStore
column number from which we’ll draw data, andactiveItem
is theListStore
row number (index) that’ll be selected by default.
We’ll talk more about the visibleColumn
variable and CellRenderer
s 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 CellRenderer
s 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
CellRenderer
is instantiated, - its packed into the
ComboBox
, and - we use
addAttribute()
to tell theComboBox
:- what its single column will display,
- which
CellRenderer
to 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 theComboBox
where to look for its data, and - pre-select one of the items, using
setActive()
, so theComboBox
shows 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
’sTreeModel
so we can - use its
getValueString()
function to grab the data stored in the first (0
th) 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 2024 Ron Tarrant