Jan 05 2007

By popular search

Categories: Programming
Tags: ,

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.


Jan 04 2007

Logos, webpages, widgets, oh my.

Categories: Programming
Tags: ,

Every month or so I come back and think I should setup a simple website for Ewl. So, I sit down, start to mock something up and get stuck. Stuck on the god damn logo. I just can’t think of anything. I’ve tried a few things and they all suck. (And yes, my brain is wired such that I can’t bring myself to finish the pages without having the missing pieces in place.)

What should a logo for Ewl look like? Should it taste like ham? Smell like roses? I got nothing. Do you have any interesting logo ideas for Ewl?


Jan 04 2007

He actually finished something Jim.

Categories: Programming
Tags: ,

I’ve actually managed to finish one of those many things I’ve been working on for the last while.

Ewl_Combo

I’ve had a re-write of the Ewl e17 theme on my plate for a while. I did the original version of the theme and didn’t really follow Ewl’s formatting rules, or know Edje for that matter. It was a bit of a mess.

Ewl_Tree2

Ewl_Notebook

I took a step back, wiped the slate and started fresh. The new theme, I think, looks pretty damn good. The main improvements are in the notebook tabs, tree headers and combo box. Don’t let that fool you. There have been changes all over the place, from smaller insets to a closer look with the default E17 theme.

Have a look, if you see any issues let me know.

pfritz has also finished and integrated his Ewl_Toolbar widget. It’s looking pretty good. I changed the theme around with my other e17 theme changes and I think it looks pretty cool at this point.


Dec 12 2006

Sliding into the wood chipper

Categories: Gaming, Life, Programming
Tags: ,

As usual, it’s been a busy few weeks. The condo is still a bit of a mess but it’s getting cleaner. We’re shuffling things around in the office so it looks like a tornado hit it. Lost the damn cord for the Rogers terminal. Going to have see if it appears out of the rubble.

In other news, took a quick trip up to Owen Sound on the weekend. Spent most of it gaming, although we did go over to a friends house for some drinks and cheese. Mmm, cheese. Tasty cookies too. Was a pretty good weekend. Hooked up with my parents for dinner, tried to call Mike for coffee, but no answer. The bus trip kinda sucked tho. Three hours on a bus where you sit with your knees pressed to the seat in front of you isn’t so fun. Did get through a good chunk of my book which is nice. Gaming was a lot of fun. Not something I get to do too often anymore which kinda sucks. I was pretty convinced my character was fucked for a while but managed to get it worked out.

After a good weekend of gaming I’ve been spending time contemplating running my own game. I know the other guys would love me to do it, but I just haven’t got there yet. After trolling the forums on Monte Cooks site for a while I ran across Robin’s Laws of Good Gamemastering which looked kind of interesting so I picked up the PDF version last night. Have I mentioned I love my laser printer? I’ll have to give it a read through and see what I come up with.

Thinking of running a game invariably leads me into thinking of writing a program to help run a game with. Something with the relevant stats/listings from the SRD documents along with a wiki style setup for creating notes and other stuff. The ability to generate maps would be nice. I tried to look for some good, free, mapping software for OSX last night but didn’t come across anything. We’ll see if I get started before my ADD kicks in or not.

In EWL news Nathan got the drag and drop code up to a basically complete state. There are some changes we could do, and some enhancements, but the plan is to let it sit for a while so people can poke at it and see where it bleeds. This was a pretty big chunk of work. We didn’t think so originally, but it ended up requiring a lot of changes all over the place. So, good work Nathan. Now, fix the mess I’ve made of tree2, heh.


Nov 23 2006

Delinquent

As you’ve probably noticed I’ve been delinquent with the whole blog thing again. I’ve been doing stuff to the blog, just not on the blog.

As you can see there is a new layout. I’ve converted from my custom Rails thingy to a Wordpress blog. The main reason, I can’t be bothered to update my code. Wordpress has other people working on it who can add cool things that I can use. I wrote some scripts to import all of my old posts with comments and to move my quotes over as well. So, everything should be as it was before. Managed to maintain my link names which is nice.

I’ve also started to setup a photo gallery again. I’m using Photostack and it seems to be working pretty well so far. They make it really simple to add new albums and things. I’ve only got some Amsterdam pictures up there at the moment. I’ll end up doing some theming on that too to make it match the rest of the site which is why it doesn’t show up on the sidebar yet.

The condo stuff is coming along nicely. I should be getting a call at some point today to go pick up the key (or I’ll just go up at 5pm as it has to be there then.) Everything has gone through that I know of. Now we just need to paint and move in. Almost there.

I’ve written a few EWL tutorials over on wiki.edevelop.org. They’re introductions to EWL programming, although they do touch on some of the harder things like drag and drop support. You’ll basically end up with an image viewing application (showing a grid of images and then full images when clicked on) that allows you to drag images from another program onto it to display those images. The three tutorials are: EWL Introduction, EWL Introduction II and EWL Introduction III

The last big EWL thing is a nice Ewl_Text speed improvement. The Ewl_Text widget I wrote had never been optimized, we hadn’t had a lot of text in it before. This changed when pfritz wrote the syntax highlighting for C source files. Suddenly we had a lot of formatting nodes to deal with. My original tree approach fell apart under the load and I re-wrote it to use a simple linked list about a month ago. This managed to bring the time from about 6 minutes to 6 seconds, which is good. (The actual original time was 12 minutes which another small change dropped to 6 minutes). 6 seconds ain’t bad, but Nathan did some more profiling and noticed two hot spots in the code. One was when we validate the text we’re making an extra copy and another was how I was looking up some formatting nodes when converting indexes. Well, once I got the copying removed it dropped to about 5 seconds. Once I got the lookup fixed, we managed to hit 1 second, pfritz is seeming about 0.63 seconds. So, I’m quite happy with that. It now takes longer to draw then it does to create the formatting tree.

We’ll see if I manage to get back onto a regular blogging schedule again. Still a bit hectic with the moving and all going on at the moment.


« Previous PageNext Page »