Combo Example ShotI noticed I was getting a few search hits for Ewl_Combo information so decided I’d write a quick tutorial on its usage. Nothing fancy just a couple examples of how to setup and use the MVC code that’s in Ewl.

The full source for this example can be grabbed from: Combo Example Application.

The Ewl_Combo widget has been designed around a Model-View-Controller (MVC) structure. The Ewl_Combo code itself is the controller. Ewl_Model and Ewl_View serve as the model and view respectively. Because several widgets (Ewl_Combo, Ewl_List and Ewl_Tree2) are MVC based we’ve abstracted the MVC code into Ewl_MVC. Each of the MVC widgets will use the same basic MVC calls to operate.

I’ll be explaining most of the Ewl concepts as I go along so a familiarity with Ewl, although helpful, isn’t required.

With that out of the way lets get started.

#include <Ewl.h>
#include <stdio.h>

We import our needed header files to start things off. Every Ewl program will include Ewl.h. Ewl.h includes all of the needed header files to use Ewl.

static void cb_delete_window(Ewl_Widget *w, void *ev, void *data);
static void cb_combo_changed(Ewl_Widget *w, void *ev, void *data);

static int cb_count_get(void *data);
static void *cb_fetch(void *data, unsigned int row, unsigned int column);

static Ewl_Widget *cb_constructor(void);
static void cb_assign(Ewl_Widget *w, void *data);

static void combo_test_icon_set(Ewl_Icon *icon, const char *data);

There are a few different callbacks that we’re defining here. There are the general Ewl callbacks and then the Ewl_Model and Ewl_View related callbacks.

The general callbacks, cb_delete_window() and cb_combo_change(), have the form: void cb_name(Ewl_Widget *w, void *ev, void *data). Every time you use an ewl_callback_*() function this is the signature of the callback you will supply. When the callback is called it maybe provided an event structure providing more information, the void *ev seen in the callback. More information on these structures can be seen at Ewl Callback Information.

The MVC code in Ewl adds a few extra callbacks. There are two pieces of the MVC code that we need to worry about, the model and the view.

In this example we’re going to have two combos. The first will use a pre-defined model based on an Ecore_List. The second will use a custom model that we’ll base on an array. The two callbacks we need to define for the list based model are a call to get the number of elements in our data and a call to retrieve an element at a specific row/column (the combo doesn’t use columns so we’ll be ignoring them.)

These two model callbacks are static int cb_count_get(void *data) to get the count of elements in our array. The controller will pass the data into the function which you can use to get your value. The second, static void *cb_fetch(void *data, unsigned int row, unsigned int column) is used to retrieve the data at the requested row/column. Again, the data is passed into the callback.

We’ll be creating two views in this application. The first is a custom view that we’ll define and the second is a pre-defined view. For the custom view there are two callbacks we need to be concerned about, one to construct our widget and one to assign the desired data to our widget. The constructor widget is defined as static Ewl_Widget *cb_constructor(void). Taking no parameters and returning our constructed widget. The assign function is defined as static void cb_assign(Ewl_Widget *w, void *data) taking the widget returned from the constructor and the data returned from the model we will setup our widget.

This design allows you to hold complex structures in your data structure and assign them to the views as needed since the controllers never look at what the data actually contains.

int main(int argc, char ** argv)
{
    Ewl_Widget *win, *box, *combo, *icon;
    Ewl_Model *model;
    Ewl_View *view;
    Ecore_List *data;
    int i;

    char *items[] = {
        EWL_ICON_DOCUMENT_OPEN,
        EWL_ICON_DOCUMENT_SAVE,
        EWL_ICON_EDIT_UNDO,
        EWL_ICON_EDIT_REDO,
        NULL
    };

With the declarations out of the way we can jump into the code. The char *items[] will be used as data to one of our controllers.

if (!ewl_init(&argc, argv))
{
    fprintf(stderr, "Error initializing Ewl.\n");
    return 1;
}

The first step before doing anything with Ewl is to initialize the library. This is done with a simple call to int ewl_init(int *argc, char ** argv). Passing in the argument information is optional and can be safely set to NULL. (Just note that doing this will cause Ewl to not receive any of its command line parameters.)

data = ecore_list_new();
for (i = 0; items[i] != NULL; i++)
    ecore_list_append(data, items[i]);

One of our combos will be based on an Ecore_List. We create the list using the values from the char *items[] that we declared earlier. Nothing fancy here just creating and populating the list.

win = ewl_window_new();
ewl_window_title_set(EWL_WINDOW(win), "Combo Example");
ewl_window_class_set(EWL_WINDOW(win), "combo_example");
ewl_window_name_set(EWL_WINDOW(win), "combo_example");
ewl_callback_append(win, EWL_CALLBACK_DELETE_WINDOW,
                            cb_delete_window, NULL);
ewl_object_size_request(EWL_OBJECT(win), 145, 145);
ewl_widget_show(win);

We start out by creating our window with Ewl_Widget *ewl_window_new(void). You’ll probably notice a general theme in Ewl when creating widgets. None of the *_new() take any parameters and they all return Ewl_Widget *. This was done on purpose to facilitate using them as the constructors in the Ewl_Views. With the window created we use void ewl_window_title_set(Ewl_Window *window, const char *title), void ewl_window_class_set(Ewl_Window *window, const char *class) and void ewl_window_name_set(Ewl_Window *window, const char *name) to set the windows title, name and class respectively.

We setup a single callback on the window EWL_CALLBACK_DELETE_WINDOW that will be called when the user requests to terminate the application. (If you don’t have this setup then nothing will happen when the user attempts to close the window.) We also set an initial size on the window using void ewl_object_size_request(Ewl_Object *obj, int width, int height).

Ewl defines an object hierarchy for its widgets. This means that you can use certain widgets, assuming it’s in the same hierarchy, in other widgets calls. All widgets inherit from Ewl_Widget and Ewl_Widget inherits from Ewl_Object so, all Ewl_Widget calls and all Ewl_Object calls are available to all widgets. When you use widgets this way you’ll need to cast them to the appropriate type. Each widget has an EWL_WIDGET() define setup to facilitate this casting. Consult the Ewl documentation for information on the widget hierarchies.

Finally we call void ewl_widget_show(Ewl_Widget *) to make the window visible to the user.

box = ewl_vbox_new();
ewl_container_child_append(EWL_CONTAINER(win), box);
ewl_widget_show(box);

If you started to pack widgets into an Ewl_Window without placing another container into it you’d end up with all your widgets sitting on top of each other. The layout policy for Ewl_Window is to place everything at 0, 0. To make sure all our widgets pack together nicely we’ll add a vertical box to the window. The box is created by calling Ewl_Widget *ewl_vbox_new(void).

This will create a container that lays all of its children out vertically. We then append the new box to the window, which also inherits from Ewl_Container, using void ewl_container_child_append(Ewl_Container *c, Ewl_Widget *w).

icon = ewl_icon_simple_new();
combo_test_icon_set(EWL_ICON(icon), items[0]);
ewl_container_child_append(EWL_CONTAINER(box), icon);
ewl_widget_show(icon);

We’re going to use Ewl_Icon widgets to display the selected values in our combo boxes. We just want the basic Ewl_Icon functionality so we’re using Ewl_Widget *ewl_icon_simple_new(void). See the Ewl documentation for the differences between a complex and simple icons. Everything else here is pretty standard stuff.

model = ewl_model_ecore_list_get();

We use Ewl_Model *ewl_model_ecore_list_get(void) to retrieve an Ewl_Model for use with Ecore_List data.

view = ewl_view_new();
ewl_view_constructor_set(view, cb_constructor);
ewl_view_assign_set(view, EWL_VIEW_ASSIGN(cb_assign));

Next we setup our view. The first step is to call Ewl_View *ewl_view_new(void) to create the default view structure. We then use void ewl_view_constructor_set(Ewl_View *view, Ewl_View_Constructor constructor) and void ewl_view_assign_set(Ewl_View *view, Ewl_View_Assign assign) to associate the methods we pre-defined above to the view.

combo = ewl_combo_new();
ewl_mvc_model_set(EWL_MVC(combo), model);
ewl_mvc_view_set(EWL_MVC(combo), view);
ewl_mvc_data_set(EWL_MVC(combo), data);
ewl_mvc_selected_set(EWL_MVC(combo), 0, 0);
ewl_callback_append(combo, EWL_CALLBACK_VALUE_CHANGED,
                                    cb_combo_changed, icon);
ewl_container_child_append(EWL_CONTAINER(box), combo);
ewl_widget_show(combo);

With our infrastructure setup we can now create the actual Ewl_Combo widget. The initial creation is done with a simple call to Ewl_Widget *ewl_combo_new(void). This will create and return an initialized combo widget. Since the Ewl_Combo widget inherits from the Ewl_MVC widget we can use those calls to set some information into the combo. First we associate our model using void ewl_mvc_model_set(Ewl_MVC *mvc, Ewl_Model *model) then the view using void ewl_mvc_view_set(Ewl_MVC *mvc, Ewl_View *view). We take the Ecore_List of data we created previously and set it using void ewl_mvc_data_set(Ewl_MVC *mvc, void *data). This data will be passed to the Ewl_Model calls when attempting to fetch the rows for our Ewl_Combo.

If we want our combo to display one of the items it contains by default we can use void ewl_mvc_selected_set(Ewl_MVC *mvc, unsigned int row, unsigned int column). In this example we’re displaying the first item in the list by default. (The MVC code is all 0 based.)

When an item is selected inside the Ewl_Combo widget Ewl will trigger an EWL_CALLBACK_VALUE_CHANGED callback on the combo widget. We can hook into this callback to take action when the user selects something. In this case we hook into the cb_combo_changed function and attach our icon as extra data.

icon = ewl_icon_simple_new();
ewl_container_child_append(EWL_CONTAINER(box), icon);
ewl_icon_part_hide(EWL_ICON(icon), EWL_ICON_PART_LABEL);
ewl_widget_show(icon);

The second Ewl_Icon is created in a similar fashion to the first one. The only difference is the use of void ewl_icon_part_hide(Ewl_Icon *icon, Ewl_Icon_Part part) which will cause the label to be hidden for the icon.

model = ewl_model_new();
ewl_model_count_set(model, cb_count_get);
ewl_model_fetch_set(model, cb_fetch);

The second combo will use a custom defined Ewl_Model. We do this by first calling Ewl_Model *ewl_model_new(void) and attaching the pre-defined callbacks with void ewl_model_count_set(Ewl_Model *model, Ewl_Model_Fetch fetch) and void ewl_model_fetch_set(Ewl_Model *model, Ewl_Model_Fetch fetch).

Both the model and view code provide #defines to handle casting the callbacks to the correct signature when calling the set methods. The define has the same name as the upper cased callback name minus the _set portion. So, EWL_MODEL_FETCH(), EWL_VIEW_CONSTRUCTOR().

view = ewl_label_view_get();

We’re using a pre-defined label view for the second combo. The view is retrieved by calling Ewl_View *ewl_label_view_get(void). This will provide a view to display the contents in an Ewl_Label widget. Using the label view requires the data passed in to be a const char *.

combo = ewl_combo_new();
ewl_mvc_model_set(EWL_MVC(combo), model);
ewl_mvc_view_set(EWL_MVC(combo), view);
ewl_mvc_data_set(EWL_MVC(combo), items);
ewl_container_child_append(EWL_CONTAINER(box), combo);
ewl_callback_append(combo, EWL_CALLBACK_VALUE_CHANGED,
                                cb_combo_changed, icon);
ewl_widget_show(combo);

We create the second combo in the exact same way as the first just assigning the char *item[] as the MVC data instead of the Ecore_List.

    ewl_main();
    return 0;
}

Calling void ewl_main(void) starts the Ewl main loop, and consequently our application, executing.

static void cb_delete_window(Ewl_Widget *w, void *ev, void *data)
{
    ewl_main_quit();
}

The cb_delete_window function is called when the user requests the window to be destroyed. Our only action on such a request is to call void ewl_main_quit(void). This will terminate the main loop and cause all of the Ewl widgets to get cleaned up.

static void cb_combo_changed(Ewl_Widget *w, void *ev, void *data)
{
    Ewl_Model *model;
    Ewl_Icon *icon;
    Ewl_Combo *combo;
    Ewl_Selection_Idx *idx;
    void *d;
    char *val;

    icon = data;

    d = ewl_mvc_data_get(EWL_MVC(w));
    model = ewl_mvc_model_get(EWL_MVC(w));
    idx = ewl_mvc_selected_get(EWL_MVC(w));

    val = model->fetch(d, idx->row, idx->column);

    combo_test_icon_set(icon, val);
}

When an item is selected in either of the combos cb_combo_changed() will be triggered. I’m doing a bit of extra work in this function in order to use the same code for both combos. We retrieve our Ewl_Icon to be modified from the void *data parameter. This contains the date we attached when setting up the initial callback.

void *ewl_mvc_data_get(Ewl_MVC *mvc) can be used to retrieve the data set with the void ewl_mvc_data_set(Ewl_MVC *mvc, void *data) call. This will either be an Ecore_List or char *[] depending on which combo was used.

Using Ewl_Model *ewl_mvc_model_get(Ewl_MVC *mvc) we can retrieve the Ewl_Model used by the current combo widget.

Finally, we retrieve the Ewl_Selection_Idx structure using Ewl_Selection_Idx *ewl_mvc_selected_get(Ewl_MVC *mvc) that contains the information about our selected item. Ewl_Selection_Idx contains both the row and column that was selected. We can ignore the column data in the combo as there is only ever one column. It’s idx->row that interest us. This row maps into our data array for the selected item.

We use the models fetch callback to retrieve the data that we’ll update the label with. You don’t have to do this in your application as you’ll probably know what your structure is coming into the callback. In this case we don’t know if it’s the Ecore_List or char *[] data so we’re using the model to retrieve the value to set.

We then update the icon with the given value and we’re done.

static int cb_count_get(void *data)
{
    int i = 0;
    char **d;

    d = data;
    while (d[i] != NULL) i++;
    return i;
}

The cb_count_get() function needs to return a count of the number of items in our data array. In this case we just iterate the array until we find the NULL terminator and count the number of widgets we find. In a more complex structure you may store a count variable in your data, or have other methods to get this value in a simpler manner.

static void *cb_fetch(void *data, unsigned int row, unsigned int column)
{
    char **d;
    d = data;
    return d[row];
}

When cb_fetch() is called we take the provided data structure and grab the rowth element and return it.

static Ewl_Widget *cb_constructor(void)
{
    Ewl_Widget *o;
    o = ewl_icon_simple_new();
    ewl_box_orientation_set(EWL_BOX(o), EWL_ORIENTATION_HORIZONTAL);
    return o;
}

Our combo is going to use an Ewl_Icon structure to display our information. Setting the Ewl_Icon into a horizontal orientation causes the icon to be displayed nicely beside the text.

static void cb_assign(Ewl_Widget *w, void *data)
{
    combo_test_icon_set(EWL_ICON(w), data);
}

The cb_assign() function just takes the provided data and assigns it to the given widget. You could do other work with the provided data here but in this case we just stick it into the label.

static void combo_test_icon_set(Ewl_Icon *icon, const char *data)
{
    const char *path;

    ewl_icon_label_set(icon, data);

    path = ewl_icon_theme_icon_path_get(data, EWL_ICON_SIZE_MEDIUM);
    if (path) ewl_icon_image_set(icon, path, data);
}

combo_test_icon_set() is a simple helper method to assign the given data as both an image, if available, and label to the icon.

With that the coding portion of this exercise is complete we can compile everything with the command:

gcc -o combo combo.c 'ewl-config --cflags --libs'

Executing the resulting application you should see something similar to the thumbnail in this posting.

We’ve tried to keep the combo widget, and other MVC widgets as well, as simple, yet powerful, as possible. The ability to assign custom constructors and assignment functions to the views allows you to setup your own complex widgets as needed. The use of callbacks to retrieve the amount of data and the data itself from the model allow you to setup your data in the fashion that best suits your application. You don’t need to mold it to fit into a set structure.

If you’ve got any questions or comments on the Ewl_Combo widget, or Ewl in general, you can send me an email or join #ewl on irc.freenode.net and let us know what you think.