Official algui thread.
axilmar

I'll post updates of my gui library in this thread.

Project page: https://github.com/axilmar/algui

I have added a skinning API, as well as a test skin example.

Comments/suggestions are welcomed.

Elias

Are there screenshots? Or videos like the ones Matthew made of his gui lib?

SiegeLord

Comments about the build system:

  • Handmade Makefiles were never okay, especially those that hard-code the source files in them

  • Makefile wants Allegro 5.1, there's no good reason for Allegro 5.1 to be necessary for this library

  • stricmp wasn't found on my Ubuntu

Comments about the GUI:

  • Why a separate event hierarchy? The whole point of having an Allegro GUI is that it doesn't add layers of interface bloat, in my opinion

  • This is especially evident with the timer thing... why can't you use your game timer for the GUI as well; why does the GUI need a timer of its own?

Comments about the example:

  • Get a real game loop, please

  • It doesn't draw anything except the messages for me

axilmar
Elias said:

Are there screenshots? Or videos like the ones Matthew made of his gui lib?

There are no widgets yet. I have just completed most of the major API mechanisms. You can check out the API to see if you like it, and then browse the file _main.c to see how it works.

SiegeLord said:

Comments about the build system:

The makefile was kindly donated by Bamcaig. Thank you Bamcaig. I am using Visual Studio on Windows, and I don't indent to spent any time with makefiles any time soon. I suggest you grab an IDE and just put the files to it, or, if you wish, make up the appropriate makefile and I will include it the project.

Quote:

stricmp wasn't found on my Ubuntu

Isn't stricmp a POSIX function in header string.h? why is it absent in your system?

Quote:

Why a separate event hierarchy? The whole point of having an Allegro GUI is that it doesn't add layers of interface bloat, in my opinion

What do you mean? widgets receive messages, not events. Messages have different data than Allegro events, and there are more messages than events.

For example, when you receive a mouse message, you get the mouse coordinates both relative to widget and to the screen, something Allegro doesn't offer.

Another example: when you receive drag and drop messages, you get the data source widget.

Another example: Allegro mouse moves are translated to mouse enter/move/leave events for widgets.

Quote:

This is especially evident with the timer thing... why can't you use your game timer for the GUI as well; why does the GUI need a timer of its own?

Because widgets may have embedded animations in them.

For example, a text widget will have a blinking cursor.

Another example: A list widget will scroll as long as the user holds the mouse button down outside of the widget.

Widgets will use the timer API to automatically manage timer events without the user having to do anything.

Quote:

Get a real game loop, please

You don't have to do anything special for the gui. You just pass the allegro events to it.

Quote:

It doesn't draw anything except the messages for me

Nothing is drawn most probably because the skin is not found.

What's your working directory? you have to set it to where the makefile directory is. The skin is to be found in the folder test/test-skin.

EDIT:

I replaced stricmp with strcmp. Unfortunately, stricmp is not a POSIX function.

SiegeLord
axilmar said:

Nothing is drawn most probably because the skin is not found.

The skin returned is not NULL, and I checked that all the bitmaps load fine too. It turns out I have to minimize and maximize the window to see the widgets.

Quote:

You don't have to do anything special for the gui. You just pass the allegro events to it.

How do you get the GUI to draw? As in, what function would I call to cause a full redraw of the whole GUI?

Evert
axilmar said:

Isn't stricmp a POSIX function in header string.h? why is it absent in your system?

It's not POSIX. strcasecmp might be though. Either way, there is something to be said for using Allegro's UTF-8 string routines for something like this.

Quote:

What do you mean? widgets receive messages, not events. Messages have different data than Allegro events, and there are more messages than events.

I don't know exactly what you're doing (haven't looked at the code), but the way I would make a GUI interact with Allegro's event system is to have it available as an event source, as well as providing a function that lets me pass Allegro events to the GUI handling function from the main loop. Is that what you do?

axilmar
SiegeLord said:

The skin returned is not NULL, and I checked that all the bitmaps load fine too. It turns out I have to minimize and maximize the window to see the widgets.

Perhaps there is no initial expose event under X-Windows. I added a call to draw the gui for the first time.

Quote:

How do you get the GUI to draw? As in, what function would I call to cause a full redraw of the whole GUI?

It's the function algui_draw_widget(widget).

Evert said:

there is something to be said for using Allegro's UTF-8 string routines for something like this

I was using it on skin filenames, and I didn't consider it necessary to handle unicode. I will change it in the future if there is request for it.

Quote:

Is that what you do?

Yes. The function algui_dispatch_event(widget, allegro_event) is used to dispatch the Allegro event to widgets.

I am not using the Allegro event queue for the gui though; it's not necessary. The above function dispatches the events to widgets as needed.

Matthew Leverton

Regarding events, I assume Evert is suggesting this:

ALLEGRO_EVENT_SOURCE *algui_get_event_source()
{
  return event_source;
}

(That's from my gui.)

So then the user can just look for ALGUI_BUTTON_CLICK events within an ALLEGRO_EVENT_QUEUE, etc.

I haven't looked at the source, so maybe you already are doing that.

I don't use the Allegro event system exclusively for internal calls either.

axilmar

I haven't looked at the source, so maybe you already are doing that.

No, I don't do that. Events are delivered straight to widgets via messages. They cannot be intercepted at the event loop. There is no need to do that.

Matthew Leverton
axilmar said:

There is no need to do that.

Users may disagree. ;)

I set up a dual mode, one where internal messages are sent directly via callback messages (such that the widget can ignore or cancel the event) and a secondary userland version using the Allegro events.

That is, the internal non-Allegro "events" are things that are meant to affect the behavior or implementation of the widget. The external "something happened" event use Allegro events.

axilmar

Users may disagree.

I don't see any use case for that. Perhaps you have one?

Matthew Leverton
axilmar said:

I don't see any use case for that. Perhaps you have one?

Strictly speaking, as long as there's some way to know that button Foo was clicked, Allegro events are not required. I added support simply because Allegro is my only target for the GUI and it seemed like the most natural way to do it.

So to me, the use case is simply "User processes events via the native Allegro event system", which is hardly helpful as an argument because it's circular.

Evert

I added support simply because Allegro is my only target for the GUI and it seemed like the most natural way to do it.

So to me, the use case is simply "User processes events via the native Allegro event system", which is hardly helpful as an argument because it's circular.

This.
I process all input from a loop that listens to events. I would expect any GUI I use to fit into that design.
Whether you agree and follow that is your own business, but it is what I personally would expect and want.

Trezker

Hmm, in my gui I feed the gui with allegro events but I have made my own event system for events from the widgets. Perhaps I should add to my todo list to make it possible to get gui events into an allegro queue.

I'll need to think about this some day when I'm not so tired.

AMCerasoli
axilmar said:

I didn't consider it necessary to handle unicode

That means that if I want to put "Niño" as a name of my window I'm not going to be able?

You library seems really good, but no supporting UTF-8 is an error... there is people around the world using Allegro, apart from the USA most of the from the EU.

The UTF-8 API of Allegro is nice but I think the harder part wasn't developed... Exist al_ustr_append to append a simple charter or a string of charter to the end of another string. But should also exist something like al_ustr_erase to erase the last charter from the string.

This because UTF-8 uses 1 to 4 bytes to save info... so depending of the letter you want to erase you are gonna have to delete 1,2,3 or 4 bytes... so you have to do comparisons and blablabla... Someone correct me if I'm wrong because that is what I'm doing. :P

Of course this might take you a while, so you can leave it to the end... :)

Evert

You library seems really good, but no supporting UTF-8 is an error... there is people around the world using Allegro, apart from the USA most of the from the EU.

I suspect he's actually aware of that. ;)
In fact, I'd have thought that UTF-8 would have been a no-brainer...

Matthew Leverton

To erase the last character:

al_ustr_remove_chr(str, al_ustr_offset(str, -1));

There's not much the library has to do to support UTF-8. As long as the text drawing functions call the UTF-8 functions you basically get it for free.

I chose to store all strings as ALLEGRO_USTR, but that's not really a requirement.

AMCerasoli

I knew there was an easier way...

axilmar
Evert said:

I process all input from a loop that listens to events. I would expect any GUI I use to fit into that design.

My library does exactly that: it reads events from an Allegro event queue and then dispatches them to widgets. Widget messages are not placed in the queue though, they are executed directly from the dispatch function. But the event loop is still there.

Do you have any use case for widget messages (not events) to be placed in a queue?

That means that if I want to put "Niño" as a name of my window I'm not going to be able?

Yes. The window name (actually, window id) is not for display purposes. It's for programming purposes only, i.e. skinning, debugging etc, and I think that sticking to English makes sense: it's the simplest solution that works. It only requires static C strings. Putting UTF-8 there will make it necessary to do memory management on those ids, which is unnecessary.

Quote:

but no supporting UTF-8 is an error...

All the widgets will have UTF-8 text. Actually, they will have an ALLEGRO_USTR, just like Matthew's lib.

Edgar Reynaldo
axilmar said:

Do you have any use case for widget messages (not events) to be placed in a queue?

Not in the way that everyone else is thinking of, I think. ???

I would however like to see the widgets queue messages for the user, such as a button widget queueing a button_pressed message, a slider queueing a value_changed message, and so on...

This way the user can trivially connect one widget's actions to another. Like opening or closing a window widget from a button press, or changing a color from a slider value change, so on...

If you don't do that, then the user is forced to poll every widget for it's state on every update to see what has changed just so they can make their gui interactive.

Peter Wang
axilmar said:

Putting UTF-8 there will make it necessary to do memory management on those ids, which is unnecessary.

Well, that's misleading. Static strings can contain UTF-8 encoded text.

It's just there are some troubles with MSVC in East Asian locales, the gory details of which were elucidated earlier on this forum.

Evert

I would however like to see the widgets queue messages for the user, such as a button widget queueing a button_pressed message, a slider queueing a value_changed message, and so on...

This way the user can trivially connect one widget's actions to another. Like opening or closing a window widget from a button press, or changing a color from a slider value change, so on...

If you don't do that, then the user is forced to poll every widget for it's state on every update to see what has changed just so they can make their gui interactive.

That is what I meant.

AMCerasoli

I want to try it.

What I need to install it on Windows?

GTK+ and what else?

I'm getting this:

$ make
gcc -shared -Wl,-soname,libalgui.so -o lib/libalgui.so.1 obj/algui_list.o obj/al
gui_rect.o obj/algui_tree.o obj/algui_widget.o `pkg-config --libs allegro-5.1 al
legro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1`
Package allegro-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro-5.1.pc'
to the PKG_CONFIG_PATH environment variable
No package 'allegro-5.1' found
Package allegro_font-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro_font-5.1.pc'
to the PKG_CONFIG_PATH environment variable
No package 'allegro_font-5.1' found
Package allegro_image-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro_image-5.1.pc'
to the PKG_CONFIG_PATH environment variable
No package 'allegro_image-5.1' found
Package allegro_primitives-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro_primitives-5.1.pc'
to the PKG_CONFIG_PATH environment variable
No package 'allegro_primitives-5.1' found
Package allegro_ttf-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro_ttf-5.1.pc'
to the PKG_CONFIG_PATH environment variable
No package 'allegro_ttf-5.1' found
obj/algui_widget.o: In function `draw':
c:\axilmal/src/algui_widget.c:186: undefined reference to `al_set_clipping_recta
ngle'
obj/algui_widget.o: In function `destroy':
c:\axilmal/src/algui_widget.c:207: undefined reference to `al_free_with_context'

obj/algui_widget.o: In function `destroy_timer':
c:\axilmal/src/algui_widget.c:362: undefined reference to `al_destroy_timer'
c:\axilmal/src/algui_widget.c:364: undefined reference to `al_free_with_context'

obj/algui_widget.o: In function `algui_draw_widget_rect':
c:\axilmal/src/algui_widget.c:1611: undefined reference to `al_get_clipping_rect
angle'
c:\axilmal/src/algui_widget.c:1617: undefined reference to `al_set_clipping_rect
angle'
obj/algui_widget.o: In function `algui_create_widget_timer':
c:\axilmal/src/algui_widget.c:2063: undefined reference to `al_create_timer'
c:\axilmal/src/algui_widget.c:2065: undefined reference to `al_malloc_with_conte
xt'
c:\axilmal/src/algui_widget.c:2069: undefined reference to `al_get_timer_event_s
ource'
c:\axilmal/src/algui_widget.c:2069: undefined reference to `al_register_event_so
urce'
c:\axilmal/src/algui_widget.c:2070: undefined reference to `al_start_timer'
collect2: ld returned 1 exit status
make: *** [lib/libalgui.so.1] Error 1

I'm using MSYS

axilmar

I would however like to see the widgets queue messages for the user, such as a button widget queueing a button_pressed message, a slider queueing a value_changed message, and so on...

This way the user can trivially connect one widget's actions to another. Like opening or closing a window widget from a button press, or changing a color from a slider value change, so on...

If you don't do that, then the user is forced to poll every widget for it's state on every update to see what has changed just so they can make their gui interactive.

It's not as trivial as you mention. You have to manage notification ids per widget: for every new command or option you put in the program, you have to allocate a new notification id. And this has to be done manually by the programmer. This is something that I have experience on due to using MFC, and let me tell you that it is very difficult and time consuming.

Furthermore, I am not a fan of big switch statements. They are counter productive. It's simply bad code to have switch functions with many hundreds of lines of code.

I am fan of callbacks (signals and slots actually).

In my library, users will be able to write callbacks which connect the appropriate widget actions to anything they want, including other widget actions, of course.

Well, that's misleading. Static strings can contain UTF-8 encoded text.

It's just there are some troubles with MSVC in East Asian locales, the gory details of which were elucidated earlier on this forum.

Ok, I didn't know that static strings can contain UTF-8 encoded text.

I am using ASCII strings for widget ids. Does anyone think that ASCII strings are not enough for such a job? for me, handling ASCII strings for widget ids has the following advantages:

  • no need to do memory management. With statically allocated C strings, each widget can have a pointer to a statically allocated string.


  • no need for extra string documentation regarding ids (who frees the memory, if strings need to be copied etc).


  • less memory footprint. If widget ids are allocated on the heap, then even if widget ids are the same for each widget class, the same strings will be allocated over and over.


  • some compilers are able to merge the same strings that are in different translation units.


  • vastly simpler programming regarding the parsing of skin text data. In the current code, parsing of simple ASCII text is very fast. Computations of string pointers are done by simply adding the number of characters to a pointer. This is not possible with UTF-8 strings. This also results to increased parsing speed.

In my opinion, I think that ASCII text for skin files is the right choice.

I'm getting this:

Your Allegro library installation is not the appropriate one?

AMCerasoli

hmm.. I supposed it.. but why are you using Allegro 5.1? 5.0 has just been released.

And I don't understand why when Allegro 5.0 wasn't ready, there was already a 5.1 version...

There is big advantages when using Allegro 5.1 instead of 5.0?

Thomas Fjellstrom

And I don't understand why when Allegro 5.0 wasn't ready, there was already a 5.1 version...

Odd minor releases are always WIP or development releases. All 5.1, 5.3, 5.5, 5.7, and 5.9 releases will be development, much like 4.9 was.

5.1 is the "in progress" devel branch for 5.2, 5.3 for 5.4, etc.

Its basically the older style linux kernel version release cycle.

axilmar

hmm.. I supposed it.. but why are you using Allegro 5.1? 5.0 has just been released.

The makefile was kindly donated by Bamcaig (thanks man!). I suppose he has Allegro 5.1?

Evert
axilmar said:

They are counter productive. It's simply bad code to have switch functions with many hundreds of lines of code.

This is debatable. It's certainly easier to read code that is nice and compact, but that goes for any block of code, not just switch statements.

Quote:

I am fan of callbacks (signals and slots actually).

In my library, users will be able to write callbacks which connect the appropriate widget actions to anything they want, including other widget actions, of course.

That's fine as a choice. But it's not how I would design an Allegro 5 GUI. In a sense, callbacks and handlers are how Allegro 4 did many things. Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.
Also, be aware that in the previous thread, there was quite a bit of discussion on whether callbacks lead to bad programming style. Not everyone is going to like it if you base your GUI around callbacks.

Not that you can't put an event system on top of a callback system (just inject an event from the callback), but it does feel a bit clunky.

Thomas Fjellstrom
Evert said:

Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.

Qt Has an underlying event system, which its Signals and Slots sit on top of. Often signals and slots are sent directly via a regular method call (at some point), but you can also queue them up, which are then sent via the event queue.

For a pure GUI app, signals and slots make the most sense IMO. But it can take some extra work to get a game to fit into it, if you don't just handle the main game stuff along side the gui.

axilmar
Evert said:

Allegro 5 has an event system instead. Part of the point of having an Allegro 5 GUI, to me, would be to use the event system.

Do you really want to litter your code like this?

#SelectExpand
1switch (event.type) { 2 case WIDGET1_EVENT1: 3 ... 4 case WIDGET1_EVENT2: 5 ... 6 case WIDGET1_EVENT3: 7 ... 8 case WIDGET2_EVENT1: 9 ... 10 case WIDGET2_EVENT2: 11 ... 12 case WIDGET2_EVENT3: 13 ... 14 ... 15}

This kind of code becomes unmaintainable very quickly, especially if event types must be managed manually.

Quote:

there was quite a bit of discussion on whether callbacks lead to bad programming style.

I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that.

It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful. For example:

void** algui_callback_data(void *data, ...);

In the place the widget callback is set:

algui_set_button_click(button1_click, algui_callback_data(data1, data2, NULL));

And then in the callback:

void button1_click(void **callback_data) {
    void *data1 = callback_data[0];
    void *data2 = callback_data[1];
}

However, the above does not solve the problem of type conversions.

In case of using custom events, the user event type generation can be automated: when a widget is created, it allocates a new event id by calling a specific function. The event type is stored internally in the widget. For example:

ALGUI_BUTTON *algui_create_button() {
    ALGUI_BUTTON *btn;
    ...
    btn->click_event_type = algui_next_event_type();
}

ALGUI_BUTTON *btn1 = algui_create_button();
ALGUI_BUTTON *btn2 = algui_create_button();

But then, in the message loop, one would have to do the following:

if (event.type == algui_get_button_click_event_type(btn1)) {
}
else if (event.type == algui_get_button_click_event_type(btn2)) {
}

This is slower than callbacks, and perhaps leading to giant if-then-else statements.

Anyway...I can't tell which is worse, at this point. Let me think about it. Additional comments are welcomed.

What do you think about the automated event type generation?

Evert
axilmar said:

Do you really want to litter your code like this?

That rather depends. For some widgets, I may not be interested and simply discard their events. I would probably wrap the entire thing up into a function as well.
It all depends on how customisable I want the dialog box to be.

Perhaps the best way to do it is to be able to register, or not, a widget as an event source.

Quote:

I suppose you mean the comments about callbacks leading to many global variables and many type conversions whereas messages in the queue don't need all that.

It's true that with the message queue solution, you don't need global data or type conversions. However, there is a solution: an API that creates an array of void* pointers that is passed to the callback can be extremely useful.

I would go so far as to say that you need something like this in a callback situation. Type conversions aren't such a big deal, in a sense they're simply a consequence of using C rather than C++.
Note that even Allegro's event system (if you use the user event API) passes custom data around using void pointers.

axilmar

Evert, What do you think about the automated event type generation?

EDIT:

I guess widget events better be Allegro events, i.e. placed in a queue.

But widget messages should not be Allegro events. The algui message system is about object orientation, not about events.

I really like the automated event type generation: it allows the programmer to do all the work from the main loop, but it also solves the problem of event type management.

AMCerasoli

Sorry for interrupt your deep conversation about programming...

But I want to know, I compiled an Allegro 5.1 Windows binaries, it seems that works fine... (they are attached, if someone want to test it), but I still getting:

$ make
gcc -shared -Wl,-soname,libalgui.so -o lib/libalgui.so.1 obj/algui_list.o obj/al
gui_rect.o obj/algui_tree.o obj/algui_widget.o `pkg-config --libs allegro-5.1 al
legro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1`
Package allegro-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro-5.1.pc'
to the PKG_CONFIG_PATH environment variable
etc...
etc...
etc...

What are those "allegro-5.1.pc" files?

EDIT: Binaries doesn't work, when I run my program using them, It ask me for freetype6.dll. I did something wrong when compiling allegro... FreeType should be static or something.

SiegeLord

Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc.

Peter Wang
axilmar said:

I am using ASCII strings for widget ids. Does anyone think that ASCII strings are not enough for such a job?

I agree, but..

Quote:

for me, handling ASCII strings for widget ids has the following advantages:

None of the advantages you listed have anything to do with ASCII or not, except the last. Even that is dubious. If your important characters are all within the ASCII range -- in practice, yes -- a UTF-8 string looks just like an 8-bit extended ASCII string. Non-ASCII characters are just passed through.

Anyway, this is verging on off-topic.

Edgar Reynaldo
axilmar said:

It's not as trivial as you mention. You have to manage notification ids per widget: for every new command or option you put in the program, you have to allocate a new notification id. And this has to be done manually by the programmer. This is something that I have experience on due to using MFC, and let me tell you that it is very difficult and time consuming.

Actually it is rather trivial to do. I'll use my GUI as an example since I use this system quite easily and it adds very little overhead and only requires minimal effort on the part of the user.

It starts with the WidgetMessage class :

WidgetMessage.hpp#SelectExpand
1/* Returns the next available message id value to prevent message collision. 2 Use TOPIC_* naming with 3 4 extern const unsigned int TOPIC_WHATEVER; 5 in the header and 6 7 const unsigned int TOPIC_WHATEVER = NextFreeTopicId(); 8 in the source module. 9*/ 10unsigned int NextFreeTopicId(); 11 12 13 14class WidgetBase; 15 16class WidgetMsg { 17 typedef unsigned int UINT; 18 19private : 20 WidgetBase* from; // Address of the widget returning this message. 21 UINT topic;// Whatever this is in regards to, like it being about a dialog. Each widget class is 22 // assigned a value for this field from the NextFreeTopicId function, as well as 23 // there being a USER topic for all generic notifications to the user. 24 int msgs; // Bitfield for setting message flags, or single message id. Up to the widget class. 25 26public : 27 28 WidgetMsg(); 29 WidgetMsg(WidgetBase* widget_address , UINT widget_topic , int messages); 30 WidgetMsg(const WidgetMsg& wmsg); 31 32 ~WidgetMsg() {} 33 34 inline WidgetBase* const From() const {return from;} 35 inline const UINT Topic() const {return topic;} 36 inline const int Messages() const {return msgs;} 37 38 inline bool IsAbout(const WidgetBase* widget , UINT widget_topic) const { 39 return ((from == widget) && (topic == widget_topic)); 40 } 41 42 inline bool IsMessageTopic(UINT widget_topic , int message) const { 43 return ((topic == widget_topic) && (msgs == message)); 44 } 45 46 inline WidgetMsg& operator=(const WidgetMsg& wmsg) { 47 from = wmsg.from; 48 topic = wmsg.topic; 49 msgs = wmsg.msgs; 50 return (*this); 51 } 52 53 inline bool operator==(const WidgetMsg& wmsg) const { 54 return ((from == wmsg.from) && (topic == wmsg.topic) && (msgs == wmsg.msgs)); 55 } 56 57 friend std::ostream& operator<<(std::ostream& os , const WidgetMsg& wmsg); 58 59};

WidgetMessage.cpp#SelectExpand
1 2#define NONE 0 3 4 5 6unsigned int NextFreeTopicId() { 7 static unsigned int id = NONE; 8 id += 1; 9 return id; 10}

Each widget has its own TOPIC and an enum describing the messages it may send :

In Widget.hpp

extern const unsigned int TOPIC_BUTTON_WIDGET;

enum BUTTON_MSGS {
   BUTTON_CLICKED  = 0, // Sent whenever a spring button is pushed down.
   BUTTON_HELD     = 1, // Sent whenever a spring button is held down.
   BUTTON_RELEASED = 2, // Sent whenever a spring button is released.
   BUTTON_TOGGLED  = 3  // Sent whenever a toggle button is toggled, poll for the current state.
};

In Widget.cpp

const unsigned int TOPIC_BUTTON_WIDGET = NextFreeTopicId();

When something important happens, the widget queues a WidgetMessage. QueueUserMessage is a virtual function that each widget may or may not redefine. The base class implementation of QueueUserMessage sends the message to it's parent if it has one, or if it is a top level widget then it adds the message to a list that can be retrieved by the user.

In Widget.cpp, using a very basic button as an example :

WidgetMsg Button::CheckInputs(int msx , int msy) {
   UINT ret = DIALOG_OKAY;
   if (area.Contains(msx,msy) && input_mouse_press(LMB)) {
      QueueUserMessage(WidgetMsg(this , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED));
      ret |= DIALOG_INPUT_USED;
   }
   return WidgetMsg(this , TOPIC_DIALOG , ret);
}

And finally, how the user uses the message :

In main.cpp :

#SelectExpand
1Button btn1; 2WidgetHandler gui; 3gui << &btn1;// Adds button 1 to the gui 4 5 6const WidgetMsg btn1click(&btn1 , TOPIC_BUTTON_WIDGET , BUTTON_CLICKED); 7 8// in logic loop 9 WidgetMsg wmsg; 10 while (gui.HasMessages()) { 11 wmsg = gui.TakeNextMessage(); 12 13 if (wmsg == btn1click) { 14 do_anything_we_want_here(); 15 } 16 17 }

All in all, it's very simple and all the user has to do is compare a WidgetMsg to see if a specific action occurred. If you're not interested in a specific message, don't check for it. Sure if you have a lot of messages then the comparisons can get long, but there's no reason you have to use a switch statement that includes every case of action represented by a WidgetMsg.

Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure.

This is all a whole lot more work than it is worth when you could have just compared a single message and then written local code to accomplish what you want to do.

My junior high algebra teacher taught me an important principle, KISS.
Keep It Simple Stupid. I think that principle applies here, and it's a good one to follow.

Matthew Leverton

I actually took a look at my source code again, and I see I also wrote this function:

void algui_observe(void *component, int event_type, bool (*proc)(void *, void *));

used like:

bool button_onclick(void *self, void *event)
{
  ALGUI_BUTTON *button = (ALGUI_BUTTON *) self;
  ALGUI_CLICK_EVENT *e = (ALGUI_CLICK_EVENT *) event;
  // ...
  return true;
}

algui_observe(button, ALGUI_CLICK_EVENT_ID, button_onclick);

The return code determines whether or not the event bubbles up to the parent. For example, you could observe the mouse click event on the root node of the GUI to process every click.

This step happens after the widget implementation has a chance to process events internally. That is, there's also an onclick entry in the button's vtable. It gets called when the button is clicked, which allows it to do something related to its implementation or behavior.

It can then choose to emit the event to the user, which can handle it by the above code.

I do also support processing events via an ALLEGRO_EVENT_QUEUE, but after reviewing my code and remembering past thoughts, I tend to agree with axilmar that it's not very useful (particularly as the project gets large).

axilmar

@Peter Wang:

these advantages have nothing to do with ascii indeed. They have to do with the default string type C has. Even so, I think that skin files should be kept as simple as possible, following the KISS principle.

@Edgar Reynaldo:

if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions. Your system is good, but signals and slots is alot more simpler.

However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context.

But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue.

This thing with messages/callbacks/events needs more thought.

Edgar Reynaldo
axilmar said:

if this was c++, I would definitely use signals and slots, because in c++ you have the ability to bind variables to functions.

Just because you can do something doesn't mean you should.

Edgar said:

Contrast this to using callbacks and/or signals and slots. To do this, the user has to create a struct, bind the addresses of affected data to that struct, set the address of that struct(s) for their callbacks, set the callback to their handler function, and then write the code for their callback/slot. If you decide to change what you're doing inside that callback, then you need to modify the struct to reflect the new data, modify your callback to reflect the new struct, and modify the callback to reflect the new procedure.

Like I said, using callbacks/signals and slots is more work than it's worth. Why spend the time binding data into a completely different scope just to alter it? Why? ???

axilmar said:

Your system is good, but signals and slots is alot more simpler.

Did you even read my post?

What's simpler than this?

if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) {
   do_anything_here();
}

Give me an example of how using signals and slots or callbacks is simpler than what I just showed you.

axilmar said:

However, in C, things are different.Since C lacks any kind of templates or lambdas, handling events from the main event queue makes sense, because it allows access to the program context.

At least we agree on this.

axilmar said:

But there are cases where the event queue cannot be used. For example, when a text is inserted in a textbox, the usual approach is to invoke a user callback that validates the insertion. I cannot see how this will work with the event queue.

This is one of the few times that an immediate response is necessary and should bypass a message system. However, the widget could be designed to validate the data itself as well.

axilmar

What's simpler than this?

This is a lot simpler (in c++):

button.click += slot(myFunction, myData1, myData2);

EDIT:

Added the following things in the library:

  • a resource manager; it uses reference counting to manage named resources. This is necessary in order to share resources (bitmaps and fonts) between widgets, especially if loaded through a skin.


  • a logging API. Used for debugging.


  • a 'display' widget, which serves as a top-level widget. It has a background color and an optional background bitmap.

I modified the test skin to load resources for the display widget.

The example contains many changes:

  • a game loop that refreshes 60 times per second.


  • init and cleanup functions.


  • the display widget in action.

Here is an image:

{"name":"YDchK.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8.png","w":652,"h":518,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/5\/7\/5725c68df582c50869db47eccef7ecd8"}YDchK.png

The background is loaded from the skin.

AMCerasoli

Cheese!! I want it! I want it!

I can't manage to install the library!

Can I compile the library without using pkg-config?

I downloaded the GTK+ binaries so now I have pkg-config but still don't working...

All-in-one bundles
If you find choosing, downloading and unpacking the individual zip archives below a chore, there are all-in-one bundles of the GTK+ stack including 3rd-party dependencies, both of GTK+ 2.16 and 2.22. The bundles contain both run-time and developer files. Many of the developer files are relatively irrelevant. If you intend to redistribute the GTK+ run-time, you need to figure out which files you can leave out yourself. A new bundle will ideally be provided here whenever one of the member packages has been updated.

SiegeLord said:

Those are files used for pkg-config program. I don't think they are made anywhere else except in Linux. Just ditch the Makefile and compile it using gcc.

I don't know how makefile works :-[

Thomas Fjellstrom

I downloaded the GTK+ binaries so now I have pkg-config but still don't working...

Why would you need or want GTK? I'm confused.

Quote:

I don't know how makefile works

You don't need to know how make works.

SiegeLord

By ditching the Makefile I meant deleting it from your system with extreme prejudice.

AMCerasoli

Well first I saw this:

Package allegro-5.1 was not found in the pkg-config search path.
Perhaps you should add the directory containing `allegro-5.1.pc'
to the PKG_CONFIG_PATH environment variable

Then I saw this

LIBS = `pkg-config --libs allegro-5.1 allegro_font-5.1 allegro_image-5.1 allegro_primitives-5.1 allegro_ttf-5.1`

etc...

${OBJDIR}/_main.o: _main.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_list.o: ${SRCDIR}/algui_list.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_rect.o: ${SRCDIR}/algui_rect.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_tree.o: ${SRCDIR}/algui_tree.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

${OBJDIR}/algui_widget.o: ${SRCDIR}/algui_widget.c
${CC} ${CFLAGS} `pkg-config --cflags allegro-5.1` -c -o $@ $<

Then went to Google an read this:

Quote:

pkg-config is computer software that provides a unified interface for querying installed libraries for the purpose of compiling software from its source code. pkg-config was originally designed for Linux but is now also available for the various BSDs, Microsoft Windows, Mac OS X, and Solaris.

And thought... "Oh I need pkg-config", still reading and found out that came with GTK+ A library to create GUI, And thought "yhea make sense" and I download it... And now I'm here...

Is something wrong in my brain algorithm?

Thomas Fjellstrom

Is something wrong in my brain algorithm?

Yes.. Instead of trying to fetch GTK to get pkg-config, you could have just grabbed pkg-config, or edited out the pkg-config bits and replaced then with the right -lblah strings.

Or manually compiled the code at the command line.

Edgar Reynaldo
axilmar said:

This is a lot simpler (in c++):

button.click += slot(myFunction, myData1, myData2);

You forgot all the code that goes with it :

#SelectExpand
1class myData1 { 2public : 3 int* int1; 4 double* double1; 5 myData1(int* int_ptr , double* double_ptr) : int1(int_ptr) , double1(double_ptr) {} 6} 7 8class myData2 { 9public : 10 bool* bool1; 11 Object* object1; 12 myData2(bool* bool_ptr , Object* object_ptr) : bool1(bool_ptr) , object1(object_ptr) {} 13} 14 15void MySpecialFunction(myData1* md1 , myData2* md2); 16 17// in main 18int special_int; 19double special_double; 20bool special_bool; 21Object special_object; 22 23myData1 md1(&special_int , &special_double); 24myData2 md2(&special_bool , &special_object); 25 26button.click += slot(MySpecialFunction , &md1 , &md2); 27 28// after main 29void MySpecialFunction(myData1* md1 , myData2* md2) { 30 if (*(md2->bool1)) { 31 md2->object1->DoStuff(*(md1->int1) , *(md1->double1)); 32 } 33}

Contrast that with this :

// in main
int special_int;
double special_double;
bool special_bool;
Object special_object;

// in logic loop
   if (wmsg == WidgetMsg(&button1 , TOPIC_BUTTON , BUTTON_CLICKED)) {
      if (special_bool) {
         special_object.DoStuff(special_int , special_double);
      }
   }

So by using a message system, you saved yourself the trouble of writing two classes, initializing two objects, writing a special function just to deal with that data, and setting up a slot.

If you want to modify the behaviour of your button, it's much easier to modify the code inside an if (wmsg ==) clause rather than modifying your classes, modifying your class instances, modifying your special function and resetting the slot to reflect the new function and data.

The whole point of writing a function is to either modularize code, or to make reusable code. Functions that use special classes holding data specific to your program will almost never be reusable, so the only reason left is to modularize code. The problem is, code inside an if clause is just as modular as code inside a function, so there's really no point in making up a function, classes, extra object instances just to refer to data, and signals and slots to connect local data with your function.

So you tell me which is simpler. Like I said before, why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with?

axilmar

Edgar, why did you go to all that trouble? you could have simply written this:

button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));

why bind local data to another scope to deal with it there when you could have just dealt with it locally to begin with?

Signals and slots are a lot more simpler and better than handling events in a queue:

  • no need to manage event ids.

  • no need to write huge switch or if-then-else statements.

  • no need to have two types of callbacks (immediate callbacks and events from the event queue).

  • signals and slots allow for putting together the initialization of an object and its signals connections; with events in a queue, this binding is far away from the place objects are initialized.

  • signals and slots are better for the model-view-controller pattern: objects can be instantiated at one place and then passed to views, and those views can monitor the model by adding signals to them; signals and slots is a more object-oriented solution.

  • signals and slots work independently of the main event loop: objects can have signals and slots in other threads that don't have an event loop.

  • less memory footprint: with events, you need to constantly allocate and free memory for each event structure, fragmenting memory; with signals, you do the allocation once (for the slot) and then reuse the same slot.

  • calling callbacks immediately leads to easier debugging; with events in a queue, it is a lot more complex: at the time the event is handled, the state that produced the event might have change, leading to confusion.

  • signals and slots are better for time-sensitive loops because slots are executed immediately, unlike events which will be executed whenever their turn comes.

  • signals and slots are better for debugging because the slot execution order does not change, unlike events which may come in any order.

  • signals and slots can be created at run-time by reading a configuration file, unlike events, because event handling code doesn't have a name, whereas slots can have a name (the name of the function or method that is bound).

  • signals and slots can be created at compile-time from a configuration file, from a GUI IDE creator, like Qt Creator does. No such luck with events.

  • C++0x will have lambda functions, which will make the bind function redundant. And lambda functions can access the local state. And lambda functions can be slots, unlike events, so there is no need to modify existing code to accommodate lambda functions.

  • when using signals and slots, objects in slots can be bound using weak pointers; when the objects are deleted, the slots are invalidated and removed from the signals they belong, and thus only the necessary signals remain to run. This can't be done with events: the event code will be there, even if it will not be executed since the object it references is no longer alive.

  • signals and slots is a lot more flexible system: slots can be added to signals from any place in a program; with events, all the necessary event handling code must be in the same place. You can't add new event handling code in a running system.

  • signals and slots works better with plugins (i.e. DLLs): just pass the signal to the plugin object, which will add a slot to invoke the necessary plugin function. With events, you have to imitate slots (i.e. registering callbacks).

  • signals and slots can be chained: signals can be slots. Can't do this with events.

  • signals can be used as functions in functional programming style. Can't do this with events.

  • signals can be bound to arguments, producing function objects with less parameters. This leads to better code reuse than events.

  • signals are better documented than events: signals are visible as object members, and can be documented accordingly. Events are more difficult to document, since there is an event id and event struct declared separately.

  • signals are much easier to declare than events: signals are declared as one liners (signal<int, double, string>) vs events which are declared with an event type id and associated struct.

  • slots can be generic function objects, for more code reuse; generic function objects can be used in many places besides slots: callbacks, lambda functions, message queues, etc.

  • signals can be emitted in one thread, and slots can be executed in another thread, using the same expression (signal += slot). You can do this with events, but you need to invoke different functions (usually it's put-event vs post-event).

  • signals and slots don't need an event queue: you can take code that uses signals and slots from the main thread and put it in another thread without changing anything. No such luck with events.

Edgar Reynaldo
axilmar said:

Edgar, why did you go to all that trouble? you could have simply written this:

Pretend what I wrote was in C. How else would you have done it then?

axilmar said:

button->click += slot(bind(my_function, special_int, special_double, special_bool, ref(special_object)));

And how are you going to recreate that same functionality in your GUI library using C?

axilmar said:

Lots of stuff about signals and slots...

But you're using C. How will you recreate signals and slots in C?

bamccaig
SiegeLord said:

Comments about the build system:

  • Handmade Makefiles were never okay, especially those that hard-code the source files in them

  • Makefile wants Allegro 5.1, there's no good reason for Allegro 5.1 to be necessary for this library

  • stricmp wasn't found on my Ubuntu

As was pointed out, the Makefile was contributed by me. If you don't like the Makefile there then fix it or replace it with something better. That's the best that I know how to do right now, but it's better than plain bash scripts or manually typing commands every time. :P

As for the Allegro 5.1 usage, I honestly don't know anything about it... All I know is that at some point somebody (probably in IRC) pointed out 5.1 (or maybe I just saw the branch in Subversion) and I started using it. I have no objections to updating the Makefile to use 5.0 (I'd do it now, but I'm in Windows still ripping CDs).

I'm getting this:

*snip*

I'm using MSYS

The Makefile was written in Linux (Fedora 13). It's not very robust and not platform-independent. The good news is that it's open source so go ahead and fix it and contribute your fixes. ;) Or if you don't care about a build system and just want to build it, compile it manually; on the command-line or using your favorite IDE. The pkg-config system is used in Linux to fill in the link libraries in a pretty way. Allegro 4 did something similar with allegro-config, but again, I think that was Linux only. Have you considered switching to Linux? :P

axilmar

Edgar, C is a different language than C++. We were talking about C++. Whatever applies to C++ does not apply to C and vice versa.

For now, I am thinking about a simple system: callbacks. Each widget will have one or more callbacks for its 'events'.

People that like events can always install callbacks that post events to the event queue.

I prefer callbacks because there is a more universal solution than events. I like my library to be consistent.

EDIT:

Version 0.0.0.8 is available.

I changed the internals of the skin and resource manager APIs to use UTF-8. Now everything uses UTF-8.

bamccaig

Updated the Makefile to use Allegro 5.0. Also added a generic rule to build object files from source files in the src/ directory. Don't know though how to abstract the mapping of object files to the respective output so ${LIBOBJS} is still static (and updated).

** APPEND **

Just pulled the UTF-8 updates in. Now the program doesn't seem to respond to the close button or ESC key. :-X I don't know if it's a bug in the program or in the build system, but naturally I blame the program. ;)

axilmar

Thanks Bam.

@Edgar Reynaldo:

With events in the event queue, you cannot do composite widgets, like a scroll bar with buttons, where the button click event is connected to the scroll bar's value change event.

This is a very big problem with the event queue.

EDIT:

I am sure it's the program. However, it works fine on Windows, and I don't have Linux to test it.

Remove the skinning (_main.c, lines 63-64) and see if it works.

EDIT2:

I've pulled Bam's makefile.

EDIT3:

Bam, I've replaced al_ustr_empty_string() with al_ustr_new(""), because I think the string returned by al_ustr_empty_string() must not be freed. Can you try it and tell me if it works for you?

bamccaig
axilmar said:

I've pulled Bam's makefile.

Unfortunately, between the two of us we borked it. It should be fixed again though on my side. You seemed to have replaced tabs with spaces when you merged. That's generally a bad idea because Makefile's use tabs as part of their syntax. AFAIK, those tabs are necessary (I use spaces for indentation too, but have Vim configured to use tabs in Makefiles). I also broke the Makefile while trying to rearrange things, but I think I have it fixed again. I've added a branch on my end for maintaining the Makefile. A script counts the objects in the library and compares it to the source files in src/. Not the cleanest way to go about things, but it gives me peace of mind that I have them all. :P Hopefully I don't screw up and merge it into master. :P

axilmar said:

Bam, I've replaced al_ustr_empty_string() with al_ustr_new(""), because I think the string returned by al_ustr_empty_string() must not be freed. Can you try it and tell me if it works for you?

I updated again, but when I run it all I see is a background image. It doesn't seem to respond to input though (ESC, 1, 2, and 3 do nothing). I can't even close the window with the close button any more. I need to ^C from the terminal I launched it from to stop it. :(

axilmar

Do I have to wait for your pull request or can I pull the makefile now?

bamccaig
axilmar said:

Do I have to wait for your pull request or can I pull the makefile now?

You can pull it any time you want. I've been pulling from you without even checking GitHub. ;D As a general rule, a pull request is just a way to communicate to somebody that you have something you'd like them to merge with. If you know somebody else has something that you want to merge with though you can just do it.

MiquelFire

Pull Request = PM that effectively says "Pull from my fork please"

In a way, it's almost as if there's pull requests embedded in this thread :P

AMCerasoli
person said:

The Makefile was written in Linux (Fedora 13). It's not very robust and not platform-independent. The good news is that it's open source so go ahead and fix it and contribute your fixes. Or if you don't care about a build system and just want to build it, compile it manually; on the command-line or using your favorite IDE. The pkg-config system is used in Linux to fill in the link libraries in a pretty way. Allegro 4 did something similar with allegro-config, but again, I think that was Linux only. Have you considered switching to Linux?

If I had the knowledge to do my own makefile I would do it, I'm actually reading a tutorial about it.

When I try to compile it with my IDE I just get the object files (.o). shouldn't be a library files (.a)? inside the lib folder?

But now I'm trying to compile the new version and I'm getting: "Makefile:33: *** missing separator. Stop." (I just read what might be happening)

Yes I use Linux too (Trisquel) but when I distribute my software I would like to say:
"Windows Version"
"Linux Version"
Why to learn one OS if I can learn two? ;D

EDIT:

And now I'm trying to compile it with my IDE and I get:

Assertion failed: skin, file C:\axilmal\src\algui_widget.c, line 2230

EDITED AGAIN:

I got it!! I got it!!

And why there is a font file?

EDITED AGAIN:

In the file:"algui_skin.h" you wrote "an algui skin is nothing more an an Allegro config file with resource strings and a path." just if you want to change it...

axilmar

I hope I copied Bamcaig's makefile correctly this time :-).

And why there is a font file?

It's for the widgets with text.

Edgar Reynaldo
axilmar said:

Edgar, C is a different language than C++. We were talking about C++. Whatever applies to C++ does not apply to C and vice versa.

Well, really we were talking about C, because that is what you are writing your library in.

axilmar said:

For now, I am thinking about a simple system: callbacks. Each widget will have one or more callbacks for its 'events'.

This leads right back to the extra code I posted before. Making arbitrary structs just to reference data, making arbitrary un-reuseable functions, and making the user set callbacks if he wants to do anything useful with your GUI's widgets.

Signals and slots may be a more powerful and versatile mechanism in C++, but I still think events are more useful than callbacks in C. It is rather rare that you need to have reaction code immediately follow a widget event, and thus you rarely need to have callbacks. I think that you are just making things more complicated than they should be, but it's your choice.

axilmar said:

With events in the event queue, you cannot do composite widgets, like a scroll bar with buttons, where the button click event is connected to the scroll bar's value change event.

This is a very big problem with the event queue.

This is not true, and it is also not a problem.

If you have composite widgets, then set the contained widgets parent pointer to the container address. When QueueUserMessage is called from the contained widgets, their parent(the container) gets QueueUserMessage called and it can check the messages before passing them on to its parent. This way, a scrollbar can check a scroll button's messages and set the slider widget's value accordingly.

The only requirement is to set a parent pointer, and to monitor the child's messages with QueueUserMessage. It works very well in my GUI.

Trezker

Events queue is the simplest system IMHO.

I think it's a good idea to have event queue in place as the first and simplest option to handle gui events. After that you add other methods of handling events for choice. But only after you have a good working GUI system in place.

With the event queue as I designed it. When a widget has a new event it just needs to call a function and it's done. Push_event(Event(this, "I got sick"));

I think a callback system clutters up your widgets.

axilmar

Well, really we were talking about C, because that is what you are writing your library in.

Then why do you post c++ examples and comments on how you would do it in c++?

Let's not mention c++ again then. We don't need to be confused by a different language.

Quote:

This leads right back to the extra code I posted before. Making arbitrary structs just to reference data, making arbitrary un-reuseable functions, and making the user set callbacks if he wants to do anything useful with your GUI's widgets.

You can always use callbacks to post events to the main event queue.

And you can also make your data global variables instead of declaring them as local variables in a function, so as that you don't need to make arbitrary structs.

There is no practical difference between declaring variables in function 'main' and outside of 'main' anyway.

Quote:

It is rather rare that you need to have reaction code immediately follow a widget event, and thus you rarely need to have callbacks.

It's not rare at all. Take a text box, for example: immediate callbacks are needed so as that the text box controls which text goes in the widget. The alternative is to code a parser which will be used by the text box, something that is extremely more difficult to do.

Quote:

I think that you are just making things more complicated than they should be, but it's your choice.

Events are more complicated than callbacks.

You need to manage all those event types.

You need to write giant switch or if-then-else statements.

When debugging, event code will be executed at a different time from the actual event, making debugging difficult.

Quote:

This is not true, and it is also not a problem.

If you have composite widgets, then set the contained widgets parent pointer to the container address. When QueueUserMessage is called from the contained widgets, their parent(the container) gets QueueUserMessage called and it can check the messages before passing them on to its parent. This way, a scrollbar can check a scroll button's messages and set the slider widget's value accordingly.

The only requirement is to set a parent pointer, and to monitor the child's messages with QueueUserMessage. It works very well in my GUI.

Your system does not work in the following cases:

1) if a parent widget wants to handle events from grandchildren.
2) if there is no parent widget.
3) if the children want to handle events of their parent.
4) if you want a subclass of a widget to manage the event raised by a base class.
5) if you want to invoke other code after the event.

Many people here say the event queue is the simplest option, but I say that it is not. Let's write some C example code to support our arguments.

Please show me an example where events in the event queue is simpler code than callbacks.

Here is an example of mine with a textbox that uses a callback to filter inserted text:

#SelectExpand
1//insert text event 2static void text_box_insert(ALGUI_TEXTBOX *textbox, ALGUI_TEXTBOX_INSERT *insert_data, void *user_data) 3{ 4 int pos = 0; 5 int ch; 6 for(;;) { 7 ch = al_ustr_get(insert_data->text, pos); 8 if (ch == '\0') break; 9 if (!isdigit(ch)) { 10 insert_data->ok = false; 11 break; 12 } 13 pos += al_utf8_width(ch); 14 } 15} 16 17//textbox 18ALGUI_TEXT_BOX *text_box; 19 20//main 21int main() { 22 //bla bla bla init, create widgets etc 23 24 //create the text box 25 text_box = algui_create_text_box(); 26 algui_set_text_box_insert(text_box, text_box_insert, NULL); 27 28 //event loop 29 for(;;) { 30 al_wait_for_event(&event); 31 switch (event.type) { 32 ... 33 } 34 } 35}

Please show me how to do the above with events.

And also show me one thing that cannot be done with callbacks just as easily as with events.

Trezker

That looks like your making custom behavior for handling input to the text box widget. But I don't see how you're getting the input for it...

My widgets have a single input function that takes all allegro events.

Each widget can have its own event queue. So you can have a controller with an event queue that only gets events from one specific widget. But the widgets also send their events to the parent, which sends to its parent and so on. So you can plug in an event queue at any level and get events from the whole interface or just a part of it.

axilmar
Trezker said:

That looks like your making custom behavior for handling input to the text box widget. But I don't see how you're getting the input for it...

A function takes an allegro event and a widget and sends messages to the widget or its children, depending on the event type.

Edgar Reynaldo
axilmar said:

You can always use callbacks to post events to the main event queue.

So the user should recreate event id's for all of your widgets just so they can use a style that works better for them? No thanks.

axilmar said:

And you can also make your data global variables instead of declaring them as local variables in a function, so as that you don't need to make arbitrary structs.

There is no practical difference between declaring variables in function 'main' and outside of 'main' anyway.

Why should I litter my code with global variables just so I can use your GUI library cleanly? There is a practical difference between local and global variables, global variables are harder to track because they are declared away from where they are used. I prefer to work locally if I can, and only use global variables if they are necessary to share information between functions.

So here are the choices that you are giving your users :
A) Write arbitrary structs and functions to use in a callback
B) Use lots of global variables
C) Recreate events on their own

Those don't look like choices I would want to make if I were using your library.

axilmar said:

It's not rare at all. Take a text box, for example: immediate callbacks are needed so as that the text box controls which text goes in the widget. The alternative is to code a parser which will be used by the text box, something that is extremely more difficult to do.

So far, a text box is the only example of a widget that may need a callback that you've given. If that's the only widget that needs it, I'd say it is still pretty rare. In my text_input class, I have three parsers, one for integer input, one for floating point input, and one for regular string input. While they took more than a few minutes to write, they cover all the basic input needs. There's no callback involved, but I could extend it to do so if necessary for custom data. Like I said earlier, if you really need a callback that's fine, but if you don't then why trouble the user with one.

axilmar said:

Events are more complicated than callbacks.

You need to manage all those event types.

You need to write giant switch or if-then-else statements.

I think our definitions of simple disagree.

When I write my GUI editor, the widgets will have functions to save their constructors and accompanying code to a source file via an ostringstream. Each widget event that can be produced by a widget will be written to the stream for them, making managing event types very, very easy. Also, the events are named well, and there will be no confusion as to what has happened with each event.

In my current GUI test program, I have 77 widgets and 40 different messages that I check for. It has not been a problem to keep them separate, nor is it a problem to modify the code that takes place when the events are detected. So there's a list of 40 if statements in my code, so what? It's better than having 40 different functions and 40 different structs and setting 40 callbacks. Don't tell me that the processor can't handle 40*60 if statements per second, because it can handle millions.

axilmar said:

When debugging, event code will be executed at a different time from the actual event, making debugging difficult.

Well, I can't speak for you, but I haven't had any problems debugging my program because of events.

axilmar said:

Your system does not work in the following cases:

1) if a parent widget wants to handle events from grandchildren.
2) if there is no parent widget.
3) if the children want to handle events of their parent.
4) if you want a subclass of a widget to manage the event raised by a base class.
5) if you want to invoke other code after the event.

I will respond to each case :

1) A widget gets the events of all of it's children, and all of it's children's children, and so on... So technically, it could handle it's grandchilren's events. It would need to know their address and type, but it could do so. To this date, I have never needed to handle events more distant than parent to child, and it is probably a design flaw if you need to do something like that. Also, a parent can intercept and retransmit an altered message, thereby passing the message from grandchild to grandparent anyway.

2) If there is no parent widget, then it is a top level widget. In my GUI, all top level widgets should be WidgetHandlers, which queue the messages for the user. You can use a standalone widget if you really want to, but then you will not receive it's messages and you have to handle everything that the WidgetHandler would have handled for you. There's really no reason not to use a top level WidgetHandler though, so it doesn't bother me. The user will know that if they use standalone widgets, they will have to manage many things on their own.

3) No child should ever handle a parent's event, and I don't see any reason why they should want to either.

4) Base classes shouldn't really be sending events, and even if they are, the derived class will probably override the base class method that sends that event anyway.

5) I don't understand what you are saying with this point. The widget/user can invoke whatever code they want to after the event occurs.

axilmar said:

Many people here say the event queue is the simplest option, but I say that it is not. Let's write some C example code to support our arguments.

Please show me an example where events in the event queue is simpler code than callbacks.

Okay, I'll take an example from my GUI. It's in C++ because even if I was using your C library, I would still write my code in C++. You can imagine it in C just as well, so I'll leave that for an exercise to the reader.

This is what happens when the user changes some HSV sliders.

#SelectExpand
1 if ((wmsg == hs_delta) || (wmsg == ss_delta) || (wmsg == vs0_delta) || 2 (wmsg == vs1_delta) || (wmsg == vs2_delta) || (wmsg == vs3_delta) || (wmsg == vs4_delta) || (wmsg == vs5_delta)) { 3 set_default_hsv_vcolor(SDCOL , vslider0.GetValue()); 4 set_default_hsv_vcolor(BGCOL , vslider1.GetValue()); 5 set_default_hsv_vcolor(MGCOL , vslider2.GetValue()); 6 set_default_hsv_vcolor(FGCOL , vslider3.GetValue()); 7 set_default_hsv_vcolor(HLCOL , vslider4.GetValue()); 8 set_default_hsv_vcolor(TXTCOL , vslider5.GetValue()); 9 huevalue.SetText(hue_slider.GetValue() , min_width , num_decimals); 10 satvalue.SetText(sat_slider.GetValue() , min_width , num_decimals); 11 vs0value.SetText(vslider0.GetValue() , min_width , num_decimals); 12 vs1value.SetText(vslider1.GetValue() , min_width , num_decimals); 13 vs2value.SetText(vslider2.GetValue() , min_width , num_decimals); 14 vs3value.SetText(vslider3.GetValue() , min_width , num_decimals); 15 vs4value.SetText(vslider4.GetValue() , min_width , num_decimals); 16 vs5value.SetText(vslider5.GetValue() , min_width , num_decimals); 17 hsv_colors = DefaultWidgetHSVColors(hue_slider.GetValue() , sat_slider.GetValue()); 18 gui.UseWidgetColorset(&hsv_colors); 19 gui.QueueUserMessage(colselect); 20 }

And now, contrast my simple message based code with what you would have to use with a callback mechanism :

#SelectExpand
1// at top of main.cpp 2class UserData { 3public : 4 Slider<float>* vsl[6]; 5 Slider<float>* hsl; 6 Slider<float>* ssl; 7 TextWidget* vsvalue[6]; 8 TextWidget* hvalue; 9 TextWidget* svalue; 10 WidgetColorset* hsv_cols; 11 WidgetHandler* _gui; 12} 13 14void CallBack(void* caller , void* data); 15 16void InitUserData(UserData* ud , Slider<float>* vsl0 , Slider<float>* vsl1 , Slider<float>* vsl2 , 17 Slider<float>* vsl3 , Slider<float>* vsl4 , Slider<float>* vsl5 , 18 Slider<float>* hueslider , Slider<float>* satslider, 19 TextWidget* vsvalue0 , TextWidget* vsvalue1 , TextWidget* vsvalue2, 20 TextWidget* vsvalue3 , TextWidget* vsvalue4 , TextWidget* vsvalue5, 21 TextWidget* huevalue , TextWidget* satvalue, 22 WidgetColorset* hsv_colors , WidgetHandler* gui); 23 24// in main 25// gui setup... 26UserData ud; 27InitUserData(&ud , &vslider0 , &vslider1 , &vslider2, 28 &vslider3 , &vslider4 , &vslider5, 29 &hue_slider , &sat_slider, 30 &vs0value , &vs1value , &vs2value, 31 &vs3value , &vs4value , &vs5value, 32 &huevalue , &satvalue, 33 &hsv_colors , &gui); 34hue_slider.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 35sat_slider.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 36vslider0.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 37vslider1.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 38vslider2.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 39vslider3.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 40vslider4.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 41vslider5.SetSliderValueChangeCallbackAndData(CallBack , (void*)(&ud)); 42 43// after main 44 45 46void InitUserData(UserData* ud , Slider<float>* vsl0 , Slider<float>* vsl1 , Slider<float>* vsl2 , 47 Slider<float>* vsl3 , Slider<float>* vsl4 , Slider<float>* vsl5 , 48 Slider<float>* hueslider , Slider<float>* satslider, 49 TextWidget* vsvalue0 , TextWidget* vsvalue1 , TextWidget* vsvalue2, 50 TextWidget* vsvalue3 , TextWidget* vsvalue4 , TextWidget* vsvalue5, 51 TextWidget* huevalue , TextWidget* satvalue, 52 WidgetColorset* hsv_colors , WidgetHandler* gui) { 53 ud->vsl[0] = vsl0; 54 ud->vsl[1] = vsl1; 55 ud->vsl[2] = vsl2; 56 ud->vsl[3] = vsl3; 57 ud->vsl[4] = vsl4; 58 ud->vsl[5] = vsl5; 59 ud->hsl = hueslider; 60 ud->ssl = satslider; 61 ud->vsvalue[0] = vsvalue0; 62 ud->vsvalue[1] = vsvalue1; 63 ud->vsvalue[2] = vsvalue2; 64 ud->vsvalue[3] = vsvalue3; 65 ud->vsvalue[4] = vsvalue4; 66 ud->vsvalue[5] = vsvalue05; 67 ud->hvalue = huevalue; 68 ud->svalue = satvalue; 69 ud->hsv_colr = hsv_colors; 70 ud->_gui = gui; 71} 72 73 74 75void CallBack(void* caller , void* data) { 76 UserData* ud = (UserData*)data; 77 if (ud) { 78 set_default_hsv_vcolor(SDCOL , ud->vsl[0]->GetValue()); 79 set_default_hsv_vcolor(BGCOL , ud->vsl[1]->GetValue()); 80 set_default_hsv_vcolor(MGCOL , ud->vsl[2]->GetValue()); 81 set_default_hsv_vcolor(FGCOL , ud->vsl[3]->GetValue()); 82 set_default_hsv_vcolor(HLCOL , ud->vsl[4]->GetValue()); 83 set_default_hsv_vcolor(TXTCOL , ud->vsl[5]->GetValue()); 84 ud->hvalue->SetText(ud->hsl->GetValue() , min_width , num_decimals); 85 ud->svalue->SetText(ud->ssl->GetValue() , min_width , num_decimals); 86 ud->vsvalue[0]->SetText(ud->vsl[0]->GetValue() , min_width , num_decimals); 87 ud->vsvalue[1]->SetText(ud->vsl[1]->GetValue() , min_width , num_decimals); 88 ud->vsvalue[2]->SetText(ud->vsl[2]->GetValue() , min_width , num_decimals); 89 ud->vsvalue[3]->SetText(ud->vsl[3]->GetValue() , min_width , num_decimals); 90 ud->vsvalue[4]->SetText(ud->vsl[4]->GetValue() , min_width , num_decimals); 91 ud->vsvalue[5]->SetText(ud->vsl[5]->GetValue() , min_width , num_decimals); 92 *(ud->hsv_cols) = DefaultWidgetHSVColors(ud->hsl->GetValue() , ud->ssl->GetValue()); 93 ud->_gui->UseWidgetColorset(ud->hsv_cols); 94 ud->_gui->QueueUserMessage(colselect); 95 } 96}

axilmar said:

Here is an example of mine with a textbox that uses a callback to filter inserted text:

Like I said before, if you really need a callback, use one. However, if it doesn't make things easier and simpler, then don't use a callback.

axilmar said:

Please show me how to do the above with events.

You picked one example where it's more appropriate to use a callback, but that doesn't prove that using callbacks is always appropriate however.

axilmar said:

And also show me one thing that cannot be done with callbacks just as easily as with events.

Just compare the two sets of code above. Which would you prefer?

Trezker

No widgets should ever handle events from any other widgets, they're just dumb views. All GUI event handling should be outside your views in controller code.
Widgets handle pure input and timer events.

There are some things I don't count in this. I don't know how to name it, the view internal behavior. Like, a text box does change itself on user input without a controller, a button gets pressed, a slider moves etc... It would be a real pain if the GUI user would have to make his own code for all this, that would defeat the purpose of even making a GUI library.

There's also the layout system, where containers and their children interact to handle automatic resizing and positioning. This is not handled through events or callbacks at all. Its a regular system of function calls up and down the widget hierarchy.

The only time widgets handle events from other widgets is if it's a composite widget. But then those events aren't even supposed to reach outside the GUI system, they're internal to a single composite widget.

EDIT:
If you put your widgets in a std container.

if("moved" == event.type && std::find(sliders.begin(), sliders.end(), event.source) != sliders.end())
{
   //Controll code
}

axilmar

So the user should recreate event id's for all of your widgets just so they can use a style that works better for them? No thanks.

There is no 'recreation' involved. It's 'creation'. My gui library will not contain any event ids.

Quote:

Why should I litter my code with global variables just so I can use your GUI library cleanly?

Why should I litter my code with local variables just so I can use your gui library cleanly?

Quote:

There is a practical difference between local and global variables, global variables are harder to track because they are declared away from where they are used.

With your system, main() will be so long that the effect will practically be the same.

I don't know about you, but I consider long functions badly written code. I always break up long functions into smaller functions, so, for me, having global variables makes more sense.

Variables in main are there for the duration of the program anyway, so there is no practical difference with globals.

I also do the following: I always have comment headers that define program sections, and I always put my global variables into one section, so actually it's a lot clearer than having local variables in the main function.

Quote:

So far, a text box is the only example of a widget that may need a callback that you've given.

There may be others. I can't tell how many they are if I don't do a complete design of the gui library.

Just now I can think of these other cases where callbacks are needed:

  • popup context menus embedded into widgets. For example, in a text box, the right click usually leads to a menu with cut/copy/paste/select all commands. With callbacks, it is much easier to create such a menu which goes hand-in-hand with the text box widget. With events, one would need to handle the cut/copy/paste/select all commands and then invoke a function of the widget to perform the action.


  • dynamic population of lists with items.


  • composite widgets like a scroll view with buttons.

etc

Quote:

When I write my GUI editor, the widgets will have functions to save their constructors and accompanying code to a source file via an ostringstream. Each widget event that can be produced by a widget will be written to the stream for them, making managing event types very, very easy. Also, the events are named well, and there will be no confusion as to what has happened with each event.

But when you want to delete widgets, their event types will require management.

Look, I have had quite a few problems with MFC the last 12 years that I am forced to use it. MFC uses event types/notification ids all over the place. I can't tell you how fast they can become problematic.

Quote:

In my current GUI test program, I have 77 widgets and 40 different messages that I check for.

Well, a game or application may have ten times more widgets.

Quote:

So there's a list of 40 if statements in my code, so what?

How long is your main()? if it's anything like more 40 lines, you need to break it up. Otherwise, it can quickly become unmaintainable.

Quote:

To this date, I have never needed to handle events more distant than parent to child, and it is probably a design flaw if you need to do something like that.

So I guess you never had layout management in your code, where there is a layout widget between the parent and child.

Quote:

If there is no parent widget, then it is a top level widget.

What if the events should be handled by a non-widget?

Quote:

No child should ever handle a parent's event, and I don't see any reason why they should want to either.

Why not? a child widget in a top-level window may cancel its operation when the top-level window is closed (for example). It's good code to be able to make the child respond to a parent's widget, because this code can be easily reused. It's a more object-oriented design.

Quote:

Base classes shouldn't really be sending events, and even if they are, the derived class will probably override the base class method that sends that event anyway.

Are you telling me that a Button base class should not send a click event, even if I subclassed it to change the way it is drawn?

I think you should re-examine your design ideas.

Quote:

I don't understand what you are saying with this point. The widget/user can invoke whatever code they want to after the event occurs.

But the problem is when you break up a well defined procedure. For example, a widget does this:

  • receive an event

  • get input from the user

  • process input with the help of a callback

  • raise another event

With callbacks, the procedure can be embedded into the widget's logic. With your design, the steps 1-2 happen inside the widget, the step 3 happens from the event queue, and step 4 does not happen unless the user handles the event.

Quote:

And now, contrast my simple message based code with what you would have to use with a callback mechanism :

You are doing extra unnecessary work. Here is how I would code it with callbacks:

#SelectExpand
1void callback() { 2 if ((wmsg == hs_delta) || (wmsg == ss_delta) || (wmsg == vs0_delta) || 3 (wmsg == vs1_delta) || (wmsg == vs2_delta) || (wmsg == vs3_delta) || (wmsg == vs4_delta) || (wmsg == vs5_delta)) { 4 set_default_hsv_vcolor(SDCOL , vslider0.GetValue()); 5 set_default_hsv_vcolor(BGCOL , vslider1.GetValue()); 6 set_default_hsv_vcolor(MGCOL , vslider2.GetValue()); 7 set_default_hsv_vcolor(FGCOL , vslider3.GetValue()); 8 set_default_hsv_vcolor(HLCOL , vslider4.GetValue()); 9 set_default_hsv_vcolor(TXTCOL , vslider5.GetValue()); 10 huevalue.SetText(hue_slider.GetValue() , min_width , num_decimals); 11 satvalue.SetText(sat_slider.GetValue() , min_width , num_decimals); 12 vs0value.SetText(vslider0.GetValue() , min_width , num_decimals); 13 vs1value.SetText(vslider1.GetValue() , min_width , num_decimals); 14 vs2value.SetText(vslider2.GetValue() , min_width , num_decimals); 15 vs3value.SetText(vslider3.GetValue() , min_width , num_decimals); 16 vs4value.SetText(vslider4.GetValue() , min_width , num_decimals); 17 vs5value.SetText(vslider5.GetValue() , min_width , num_decimals); 18 hsv_colors = DefaultWidgetHSVColors(hue_slider.GetValue() , sat_slider.GetValue()); 19 gui.UseWidgetColorset(&hsv_colors); 20 } 21}

The variables touched inside the callback would be globals.

Quote:

Like I said before, if you really need a callback, use one. However, if it doesn't make things easier and simpler, then don't use a callback.

I'd like my library to be consistent. If there was something that the event queue offered me that I cannot do with callbacks, then I'd consider that solution.

Quote:

You picked one example where it's more appropriate to use a callback, but that doesn't prove that using callbacks is always appropriate however.

As I've shown you, callbacks cover more use cases than events. Callbacks are a more generic solution.

Quote:

Just compare the two sets of code above. Which would you prefer?

Signals and slots coupled with lambda functions. Since I can't have that in C, I prefer callbacks.

I had quite a lot of issues with MFC. The event-id model just doesn't scale that well.

And then, there is the issue of turning a design of events into object-oriented code. This is where the event system completely breaks down. MFC message maps anyone?

Martin Kalbfuß

which is hardly helpful as an argument because it's circular.

You could use two queues. This would brake the cicle.

hardware/system interface -> GUI-queue -> GUI -> game-queue -> game

The gui chatches the user input and either consumes it, or push it into the game queue. The only event source of the game queue is the GUI. I think the system does the same. If you resize a window it's done with the mouse. But you get an resize-event.

This way you could even run the gui in it's own thread.

Evert
axilmar said:

I don't know about you, but I consider long functions badly written code. I always break up long functions into smaller functions, so, for me, having global variables makes more sense.

To me, long, convoluted functions and global variables are both examples of bad code, with global variables by far the greater evil of the two.

Quote:

How long is your main()? if it's anything like more 40 lines, you need to break it up.

This is fine as a guideline, but sometimes it's unavoidable that a function becomes longer than that. That's ok, as long as the overall structure is clear.

Trezker

Of course you don't put all the event handling in main. You make controllers that handle specific tasks.

while()
{
   ALLEGRO_EVENT event;
   while (al_get_next_event(event_queue, &event))
   {
      root->Handle_event(event);
   }
   for(Controllers::iterator i = controllers.begin(); i != controllers.end())
   {
      (*i)->Update();
   }
   root->Render();
}

axilmar

@Evert: in the case of an event queue, main() variables are equal to global variables. There is no practical difference between the two.

@Trezker: I like your idea, but I guess Edgar Raynaldo will disagree with you, I am sure :).

Evert
axilmar said:

in the case of an event queue, main() variables are equal to global variables. There is no practical difference between the two.

???

Matthew Leverton

I declare a stalemate. 8-)

axilmar

Main() variables and global variables are the same, when it comes to using an event queue: all the code will be in the main loop, which has access to all the main() variables or the global variables. So, one might put the main() variables as globals.

Evert
axilmar said:

all the code will be in the main loop,

No...

Quote:

which has access to all the main() variables or the global variables. So, one might put the main() variables as globals.

Oh, you mean: if I look at one function, from the point of view of that function, it doesn't matter whether the variables used in it are global or local?
That is true, of course. It is also an empty statement. By the same logic, you could get rid of function arguments and local variables alltogether and use globals exclusively everywhere.
Whatever handles the message from the GUI, be it an event handler or a callback, obviously needs to have access to whatever object it needs to manipulate as a result of the message. That is trivially the case if the object is in the same scope, or, alternatively, if it is passed in as a variable.

I strongly prefer the latter, since it's far easier to write reusable and maintainable code that way.

I declare a stalemate.

More of a draw by repetition. :P

Edgar Reynaldo
axilmar said:

There is no 'recreation' involved. It's 'creation'. My gui library will not contain any event ids.

So all of your users that prefer clean local code have to create events on their own. Thanks a lot.

Once again, here are the choices that you are giving your users :
A) Write arbitrary structs and functions to use in a callback
B) Use lots of global variables
C) Recreate events on their own

These are not nice choices to have to make for a user. Here are the choices you give your users when you use events :
A) Write clean local code
B) Use a callback function if they wish to, not because they have to
C) Use global or local variables as they wish, and not be forced to use globals just to work cleanly with your library.

These are much better choices to provide to your users. They can respond in whatever way works best for them, and they are not limited by callbacks.

axilmar said:

Why should I litter my code with local variables just so I can use your gui library cleanly?

You're kidding right? You use local variables because they are only supposed to last as long as the function that uses them. You use global variables because they outlive function calls. Don't use global variables just because they have global access. Say my gui handling wasn't done in main, but in a separate callable function, like for opening a dialog. Why should all the widgets in that dialog outlive the function they were designed for? It's a waste of memory, and bad practice. Besides all that, you can use variables of any scope when working with my library, but your library forces the use of global variables.

axilmar said:

I don't know about you, but I consider long functions badly written code. I always break up long functions into smaller functions, so, for me, having global variables makes more sense.

In some cases it makes sense to break up functions, but with widget messages your code is separated by if clauses. They're just as readable and just as modular as separate function calls, but you don't need to look somewhere else to find out what they are doing.

axilmar said:

Variables in main are there for the duration of the program anyway, so there is no practical difference with globals.

Go back to the example I just gave about using a function to bring up a dialog. The variables used by that function should not outlive it.

axilmar said:

Just now I can think of these other cases where callbacks are needed:

  • popup context menus embedded into widgets. For example, in a text box, the right click usually leads to a menu with cut/copy/paste/select all commands. With callbacks, it is much easier to create such a menu which goes hand-in-hand with the text box widget. With events, one would need to handle the cut/copy/paste/select all commands and then invoke a function of the widget to perform the action.

  • dynamic population of lists with items.

  • composite widgets like a scroll view with buttons.

  • You don't need a callback to have a context menu. Just make the widget own the context menu and respond to messages from the context menu. The user doesn't need to be involved in handling messages from the context menu at all, because the widget deals with them.

  • A list widget doesn't need a callback to get list values. The user sets the values on the list whenever they feel it is appropriate.

  • A widget that owns a scrollbar doesn't need a callback. It simply handles the messages that the scrollbar sends to it using QueueUserMessage. In a way, QueueUserMessage is the callback.

So the only example you can give me where a callback is necessary is with a textbox where the user wants to have a custom parser. That's fine, but it still doesn't prove that only callbacks should be used to handle messages/events from widgets.

axilmar said:

But when you want to delete widgets, their event types will require management.

You don't need to manage an event type at all. If a widget no longer exists, it will no longer send messages and the message check will simply return false and not be executed.

axilmar said:

Look, I have had quite a few problems with MFC the last 12 years that I am forced to use it. MFC uses event types/notification ids all over the place. I can't tell you how fast they can become problematic.

So what are the typical problems with using widget messages/events then? Nothing you've said about how it causes problems for you has ever caused any problems for me.

axilmar said:

Well, a game or application may have ten times more widgets.

If you use widget messages then I would have 400 if statements, you on the other hand would have 400 functions and either 400 structs or all global variables. I would know exactly where to look when important actions occur, you on the other hand have to check which callback you set for which widget and then find that function across one or many different source files.

axilmar said:

How long is your main()? if it's anything like more 40 lines, you need to break it up. Otherwise, it can quickly become unmaintainable.

Currently, for the GUI test program, the source file is 700 lines long, of which there are 310 lines to declare widgets and 118 lines to deal with widget messages. I still know exactly where everything is, and modifying it is still not a problem. This is real life, functions take more than 40 lines.

axilmar said:

So I guess you never had layout management in your code, where there is a layout widget between the parent and child.

My gui doesn't currently have any layout management, for now that is up to the user. It's very difficult for a function to know how you want to arrange your widgets unless you use some kind of system like HTML. I will have separator widgets that manage two widgets sharing the same space that divides the space between them though.

axilmar said:

What if the events should be handled by a non-widget?

I don't really know what you're talking about here. Top level widgets are WidgetHandlers and they store the messages the widgets queue. After that, do whatever you want to with them.

Axilmar said:

Edgar said:

No child should ever handle a parent's event, and I don't see any reason why they should want to either.

Why not? a child widget in a top-level window may cancel its operation when the top-level window is closed (for example). It's good code to be able to make the child respond to a parent's widget, because this code can be easily reused. It's a more object-oriented design.

In my GUI this is handled by a Shutdown function. If a child widget needs to cancel some operation during Shutdown then it will. It still doesn't require a callback. Generally, it's the responsibility of the parent to tell the child what to do, not the other way around.

Matthew Leverton

So all of your users

In the beginning, there is only one user. You have to please that one first. ::)

Edgar Reynaldo

Sorry if I'm double posting, but the server had problems with my entire post. It didn't say it was too long, it just didn't post it right. So here's the rest of my post (I'll try my best to keep it brief from now on, but I wanted to respond to everything axilmar said).

In the beginning, there is only one user. You have to please that one first.

Yeah, I guess you're right.

axilmar said:

Are you telling me that a Button base class should not send a click event, even if I subclassed it to change the way it is drawn?

I think you should re-examine your design ideas.

No, I'm not telling you that a Button base class shouldn't send an event. In the case that you want to override the Draw method only, the base class still sends the event. If a derived class wants to intercept a base class message all it has to do is redefine QueueUserMessage to check for the base class message. My design ideas are just fine, thanks.

axilmar said:

With callbacks, the procedure can be embedded into the widget's logic. With your design, the steps 1-2 happen inside the widget, the step 3 happens from the event queue, and step 4 does not happen unless the user handles the event.

Like I said before, if you really need a callback use one. But if you don't, don't. Also note that step 4 wouldn't happen if your user never set a callback either. What that proves, I don't know.

axilmar said:

The variables touched inside the callback would be globals.

So again, your system encourages either arbitrary structs or forced use of global variables. My system allows the user to use whatever suits them.

axilmar said:

I'd like my library to be consistent. If there was something that the event queue offered me that I cannot do with callbacks, then I'd consider that solution.

This works both ways - If callbacks offered me something I can't do with events then I'd use them.

axilmar said:

As I've shown you, callbacks cover more use cases than events. Callbacks are a more generic solution.

And as I've shown you, callbacks are rarely necessary. Widget messages are a more flexible solution (from the users point of view).

axilmar said:

And then, there is the issue of turning a design of events into object-oriented code. This is where the event system completely breaks down. MFC message maps anyone?

I don't know what you're talking about. There's no reason a user can't use object oriented code with widget messages. How is it that the event system breaks down?

Trezker said:

Of course you don't put all the event handling in main. You make controllers that handle specific tasks.

axilmar said:

@Trezker: I like your idea, but I guess Edgar Reynaldo will disagree with you, I am sure :).

axilmar, how perceptive of you! What's the point of using controllers when you could just use local code? It just leads back to arbitrary structs or global variables. There's no logical reason to add a layer of abstraction to something simple like messages.

I LN

Hello everyone!
Is there a complete working example of algui and Allegro 5? (only basic features)
I'm trying to learn Allegro (and since I'm at it, I figured the latest version would be better) and need a GUI.
Alternatively, if someone could indicate a simple GUI (buttons and editboxes are all I need now), It would do me very good.

Regards,
Laurentiu

PS: Using VS 2008, if it matters.
PPS: The link from http://www.allegro.cc/resource/CodeGallery/TextInput seems dead, if anyone cares

axilmar

@Edgar Reynaldo: The argument is global vs local variables, right? well, let me enumerate advantages and disadvantages of each approach:

Advantages of events posted in event queue.

  • access to the local context.

Disadvantages of events posted in event queue:

  • manual management of event id constants.

  • huge switch or if-then-else statements.

  • not good for injecting code into algorithms.

  • makes debugging asynchronous and hence more difficult than it ought to be.

  • heavy fragmentation of memory.

  • difficult to extend via C++ (see message maps vs virtual functions debates).

Disadvantages of callbacks:

  • no access to the local context.

Advantages of callbacks:

  • cleaner code by using smaller functions.

  • suitable for code injection in predefined algorithms.

  • synchronous debugging.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.

  • no need to manage constants manually.

Hmmm...sorry, I can't see how events are better.

Trezker

Once I'm done with saving and loading layouts. Declaring all the widgets in your entire program can be done with one line of code. Or at least just a few...

Something along these lines. Note that I'm not using a single global anywhere.

//Setup
Layout layout("myinterface.layout");
My_controller controller(layout);
//Main loop
layout.Get_root().Handle_event(allegro_event);
controller.Update();
layout.Get_root().Render();

Matthew Leverton

What do the layout files look like?

axilmar

Here is a compromise between the event design and the callback design:

  • a widget can have one more callbacks.

  • a callback has an event id and a callback procedure.

  • the event id is automatically generated or manually set.

  • if the callback is invoked and the callback procedure is not null, then the callback procedure is invoked, otherwise an event is posted to the event queue.

So, you event queue lovers can do this:

#SelectExpand
1int main() { 2 BUTTON *btn; 3 4 btn = create_button(); 5 6 //optionally set id 7 set_button_click_event_type(btn, MY_BUTTON_ID); 8 9 while (loop) { 10 al_wait_for_event(&event); 11 if (event.type == get_button_click_event_type_id(btn)) { 12 //bla bla 13 } 14 //or 15 if (event.type == MY_BUTTON_ID) { 16 //bla bla 17 } 18 } 19}

And we callback lovers can do this:

#SelectExpand
1BUTTON *btn; 2 3void btn_click() { 4 //bla bla bla 5} 6 7int main() { 8 9 btn = create_button(); 10 11 //optionally set id 12 set_button_click_proc(btn, btn_click); 13 14 while (loop) { 15 al_wait_for_event(&event); 16 } 17}

What do you people think?

Trezker

The layout files are xml. Users wont edit layouts manually, I'm making an editor.

Edgar Reynaldo
axilmar said:

Advantages of events posted in event queue.

  • access to the local context.

Disadvantages of events posted in event queue:

  • manual management of event id constants.

  • huge switch or if-then-else statements.

  • not good for injecting code into algorithms.

  • makes debugging asynchronous and hence more difficult than it ought to be.

  • heavy fragmentation of memory.

  • difficult to extend via C++ (see message maps vs virtual functions debates).

Disadvantages of callbacks:

  • no access to the local context.

Advantages of callbacks:

  • cleaner code by using smaller functions.

  • suitable for code injection in predefined algorithms.

  • synchronous debugging.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.

  • no need to manage constants manually.

Being a little more realistic :

Advantages of events posted in event queue.

  • access to the local context.

  • can deal with all events in a single place, limiting the amount of searching necessary to find relevant code

Disadvantages of events posted in event queue:

  • manual management of event id constants.
    There's nothing to manage about an event id. All you do is declare it :

    if (wmsg == WidgetMsg(&button , TOPIC_BUTTON , BUTTON_CLICKED)) {do_stuff();}
    

  • huge switch or if-then-else statements.
    This is not a disadvantage if you like all of your event handling in a single place.

  • not good for injecting code into algorithms.
    ??? You can use whatever code you want inside an if clause.

  • makes debugging asynchronous and hence more difficult than it ought to be.
    Debugging with events is not any more difficult if you know how to debug in the first place. Set a breakpoint inside the message check, or inside QueueUserMessage. Easy as pie.

  • heavy fragmentation of memory.
    I don't believe that allocating and deallocating memory on the stack fragments memory. Prove me wrong if you like.

  • difficult to extend via C++ (see message maps vs virtual functions debates).
    You can do whatever you like inside an if clause. Provide some proof for this one.

Disadvantages of callbacks:

  • no access to the local context.
    Which forces use of either :

    • global variables

    • pointless structs just to bind data to another context

Advantages of callbacks:

  • cleaner code by using smaller functions.
    If clauses are just as modular as functions are, without scattering code all across your source file(s).

  • suitable for code injection in predefined algorithms.
    Define code injection, and tell me why you think local event code is unsuitable for it.

  • synchronous debugging.
    Event code debugging is just as linear as call back debugging. There's still a defined order to your code, and it's just as easy to follow.

  • does not require memory allocations/deallocations.

  • easy to extend via C++.
    You can extend event code just as easily as you can callback code.

  • no need to manage constants manually.
    What are you talking about? There's nothing to manage. All you do is compare an object to a constant object.

axilmar said:

Hmmm...sorry, I can't see how events are better.

Look closer. Because I can't see how callbacks are better.

Trezker said:

//Setup
Layout layout("myinterface.layout");
My_controller controller(layout);

Sounds ambitious. How do you expect your controller to be able to set callbacks / respond to events without using a predefined set of names for your widgets in your layout file? Ie... how will you hook code up to widget actions?

axilmar said:

What do you people think?

I think it's better than locking your users into callbacks only.

Matthew Leverton

Sounds ambitious. How do you expect your controller to be able to set callbacks / respond to events without using a predefined set of names for your widgets in your layout file? Ie... how will you hook code up to widget actions?

I also use XML. Currently it's only for single elements:

ALGUI_BUTTON *b = algui_xml("<button id='close' text='Close Me' width='100' height='50' />");

Obviously you could also use the API to set it all, but the above ends up being far less verbose and easier to remember than a bunch of C calls.

Anyway, I also have this:

algui_find_by_id(void *parent, const char *id);

which could be used to find the individual elements after loading an XML layout (when I add full support).

Trezker is probably doing something similar.

Edgar Reynaldo

I use a similar mechanism in my GUI, except that const char* id is just string wname and you use FindWidgetByName(string name).

What I was getting at is that there is no way to hook widgets up to actions without using a predefined set of names in your controller, thus rendering a single controller only useful for a single set of named widgets.

Can anyone come up with a way to hook widgets up to actions at runtime without using a predefined set of names and functions?

tobing

ALGUI_BUTTON *b = algui_xml("<button id='close' text='Close Me' width='100' height='50' />");

Can you specify the text in such a way, that the text is actually taken from another file? To make localization simple. I'm using extra files that only contain the strings, so adding a language is a matter of translating strings in one file and be done. No need to recompile and sources...

Matthew Leverton

Can anyone come up with a way to hook widgets up to actions at runtime without using a predefined set of names and functions?

You mean like embedding a scripting language into the layout? (Similar to JavaScript inside HTML.) If so, that's definitely overkill for what my goals for a GUI would be.

tobing said:

Can you specify the text in such a way, that the text is actually taken from another file?

I've not given any thought to localization yet. But something like this would let you do whatever you wanted:

algui_set_text_localization(my_proc);

ALLEGRO_USTR *my_proc(const char *text)
{
  return al_ustr_new(text);
}

// in the internal algui "set title" function
window->title = algui_localization_proc(title);

then you could do whatever you wanted for localization.

tobing

Maybe... that would let you replace one string by another. Unfortunately localization is a bit more difficult, for example it may be that in one language you need different strings where in another you need the same. Or singular versus plural, which works differently in different languages, or many other problems you run into.

It depends on how much text you have, of course, and how nice you want your output to be. For me, I'm always thinking about localization in the two languages I know best, which is german and english, and I try to write it so that both work right from the start. That's not perfect, but a good start.

Then I use a numbering scheme, associating numbers to strings. The number-to-text mapping is read from a language specific file, and everything else, be it code or script (or XML in your case) would refer to the numbers used. Works really fine for me.

Matthew Leverton

Then you would do this:

algui_xml("<button text='1' />");

and your localization function would do a number to text mapping. It's up to you.

Text that requires context should be avoided. E.g.:

[ ] Choice One
[ ] Choice Two
[ ] Choice Three

| PROCESS ITEM(S) |

There the text on the button depends on the number of items selected. A better approach is to just use the text "PROCESS." Hopefully that eliminates the need for complex rules.

But in situations where it cannot be avoided, I don't see why the GUI should solve the problem for you. In that case, you'll need to pass the context along to your own localization function to get the proper string.

tobing

The GUI is not to solve the problem, I just put the string together somehow and pass it to the UI to be displayed. So in easy cases I might just take the string from the string manager (which maps IDs to strings), and in more complex cases, I might construct the string from parts taken from the string manager and then pass the result to the UI. Depends.

One of the games we are making at home is the RechenMonster (see depot) game, which trains doing computations in hour head. Aimed for kids at school, like 8.12 years maybe, but also fun for adults who want to do some training. The goal is to make this fun (not easy), and one of the points is that you need lots of text. In most cases it's quite easy though, so no problem.

There is one particular mini game inside, which is about reading the clock and times, and here different languages really do work quite differently. In this case, localization goes even beyond replacing text - you need different code (to check if an answer is correct).

But OK, this is in most cases not a problem for the games people make here, I would guess. Your suggestion to map "1" to a language dependent string will definitely do the trick in almost all cases.

axilmar

@Edgar Reynaldo:

1) you have to mange the constants TOPIC BUTTON, BUTTON CLICK, etc. And if you want to extend the library, you have to define new constants. If you then want to change the code, you have to maintain the constants.

2) even if you like all your event code in one place, it soon becomes unmanagable. Good coding practices require breaking long functions into smaller ones.

3) if i put a breakpoint inside QueueUserMessage, then the execution will be interrupted for all messages. It makes debugging more difficult.

4) i was not talking about local variables, but about messages put in the queues: the is allocation/deallocation all the time.

5) with events, you must bind c++ methods to events via a message map, which is awkward. With callbacks, it's much easier.

6)if clauses are not as modular as functions, because functions can have parameters.

7) code injection is an algorithm with hooks. you cannot insert hooks with events. You cannot specialize an algorithm with events.

For all the above, events are inferior to callbacks. Howeve, i am willing to offer a solution which covers both camps. What do you think of that?

Trezker

All the widgets in my layout has a unique name. So the controller can easily find the widgets it needs to work with.

Depending on how you want to work you could always look up the name
layout.Get_name(event.source) == "button_x"

Or store the widget
My_controller::My_controller(Layout &layout)
:button_x(layout.Get_widget("button_x"))
{}

Or you could set up callbacks in whatever way you wish. I'm pondering adding a callback feature somewhere. Perhaps in the event_queue, so it would call callbacks instead of storing events in a queue... Though if it could be used in two ways like that it wouldn't be a queue anymore.

Perhaps an Event_handler base class, with Event_queue and Event_relay subclasses or something like that. Then the widgets would only know that they're giving their events to a handler without caring about how they reach the user.

Edgar Reynaldo
axilmar said:

you have to mange the constants TOPIC BUTTON, BUTTON CLICK, etc. And if you want to extend the library, you have to define new constants. If you then want to change the code, you have to maintain the constants.

I still don't know what you mean by 'manage' the event id's. They're just a set of predefined constants at the top of each widget header. Adding new TOPIC_*'s is just adding a simple external constant, initialized by NextFreeTopicId(). The constants and enums defined by the library will most likely never change (not to say that more can't be added easily without breaking existing code).

axilmar said:

even if you like all your event code in one place, it soon becomes unmanagable.

I still haven't found this to be the case. All it takes to find an event is to do a Find in your editor for the address of the widget you want to know about.

axilmar said:

i was not talking about local variables, but about messages put in the queues: the is allocation/deallocation all the time.

Well, this is true, but it's very rare that you get more than one message per update. I would guess that the same memory slot is just reused, but I can't say for sure. I really don't think it amounts to much in the end though.

axilmar said:

with events, you must bind c++ methods to events via a message map, which is awkward. With callbacks, it's much easier.

I still don't know what a message map is. Can you give a small code example of this? Also, why can't you just call your C++ method from the if clause that handles the message?

axilmar said:

code injection is an algorithm with hooks. you cannot insert hooks with events. You cannot specialize an algorithm with events.

I don't understand this very well. Can you give me a code example? Why can't you just call your algorithm locally after specifying the hook's callback? Any code you can call with a callback can be called from an if clause.

axilmar said:

However, I am willing to offer a solution which covers both camps. What do you think of that?

Fine by me.

bamccaig
Trezker said:

The layout files are xml. Users wont edit layouts manually, I'm making an editor.

That, good sir, will depend on just how efficient your editor is. ;) It might be faster for some basic layout stuff, but if it's faster to change an attribute by opening the file in Vim then you're damn right I will. ;)

axilmar

My lib has the function algui_set_translation to set the text, which is loaded from a config file. Each widget receives the relevant message and reads the appropriate string from the config file.

@Edgar:

1)@when you make new classes and when you remove classes, when you make new event constants and delete old ones, or modify existing ones, that's what i mean about managing event ids...not at run time, but at compile time.

2) the long functions are truly difficult to maintain. Imagine having, let's say, 200 widgets...if each event takes 10 lines of code, the main loop would be 2000 lines of code! for just one loop!

3) if your widgets produce events like "value changed" when the mouse is moved, for example a slider, then hundreds, maybe thousands of message structs will be allocate each second.

4) i am posting this from my mobile phone, so it's kind of difficult to give you a message map example. You can search in google and you will find many. A message map is a way to bind messages to functions.

Calling a method of an object for each possible combination of message and method manually is impractical.

5) having to manually invoke an algorithm's continuation from the event loop is impractical.

Trezker

bamccaig: If you only want to change single attributes then I think it would be hard to make an editor that beats a text editor in usability. So in that case it's good I'm using xml. You can just open the file, search for the widgets name and change the attribute.

bamccaig

I was just being pedantic. ;)

Edgar Reynaldo
axilmar said:

@when you make new classes and when you remove classes, when you make new event constants and delete old ones, or modify existing ones, that's what i mean about managing event ids...not at run time, but at compile time.

Well, I haven't gone to too much trouble while doing this. New widget with important actions? Write one constant declaration and definition, and write one enum with the possible actions. It's really not too much to ask a widget writer to do. It's about the same amount of work it takes to add a function pointer to your class and a method to set it. The TOPIC constant never changes, and the enumerated messages almost never change, so it doesn't require much upkeep either.

axilmar said:

the long functions are truly difficult to maintain. Imagine having, let's say, 200 widgets...if each event takes 10 lines of code, the main loop would be 2000 lines of code! for just one loop!

But you would prefer to have 200 different functions instead. In my case all it takes to find the relevant code is a Find for a widget address. You would have to Find the widget declaration and then the callback function you set for it. It's not all that different I'd say.

axilmar said:

if your widgets produce events like "value changed" when the mouse is moved, for example a slider, then hundreds, maybe thousands of message structs will be allocate each second.

No, most likely only 60 per second (or whatever your update rate is) at most, since CheckInputs almost always only produces one widget message per call. And that's only one push/pop per update, which I would guess uses the same memory location for each push. If I used a vector for the message queue then it would definitely use the same memory if I used reserve(). I really don't think this is a problem, but if you can prove otherwise then I might change it.

axilmar said:

i am posting this from my mobile phone, so it's kind of difficult to give you a message map example. You can search in google and you will find many. A message map is a way to bind messages to functions.

There's no need to use a message map though, if you want to call a function when you receive a specific event, you just do it. If the users want to complicate things by using a message map, then that's their choice to do so, but they really don't have to, nor should they.

axilmar said:

Calling a method of an object for each possible combination of message and method manually is impractical.

Not just impractical, but silly as well. If you really want to , you could use a std::map<WidgetMessage , void (*) ()>. Or whatever form your callbacks take. This really doesn't make sense though, because you can just call your specific function from the if (wmsg == ...) statement.

axilmar said:

5) having to manually invoke an algorithm's continuation from the event loop is impractical.

I don't understand this. Why would you continue an algorithm from an event detection anyway? And how would you integrate a callback for a widget into an algorithm instead? Shouldn't it be the other way around? That after detecting an event or inside a callback that you then call the algorithm?

axilmar

so it doesn't require much upkeep either.

But with callbacks you don't need to write even a single constant.

Quote:

But you would prefer to have 200 different functions instead.

Yes, 200 different functions can be organized much better than a continuous function spanning, let's say, 2000 lines of code.

And with such long functions you quickly lose track of variables.

Quote:

No, most likely only 60 per second (or whatever your update rate is) at most, since CheckInputs almost always only produces one widget message per call.

With Allegro 5, as with any other event system, the rate of mouse position change does not depend on the screen refresh rate. So, it really depends on how fast the user moves the mouse and how fast the hardware can read mouse events.

Quote:

And that's only one push/pop per update, which I would guess uses the same memory location for each push. If I used a vector for the message queue then it would definitely use the same memory if I used reserve().

If you use a vector, you have two problems in your hand: a) the reallocation of the vector's internal buffer, b) the copying of elements when you remove an element from the front of the vector.

If you use a list, then you have fragmentation. The same memory slot may or may not be reused, since there can be other allocations at the same time.

Quote:

I really don't think this is a problem, but if you can prove otherwise then I might change it.

It is, in memory-tight systems. For example, the iPhone gives you 48 MB of memory to play with.

Even if it's not a problem in modern PCs, though, I don't see why there has to be an allocation, since it's not required.

Quote:

There's no need to use a message map though, if you want to call a function when you receive a specific event, you just do it. If the users want to complicate things by using a message map, then that's their choice to do so, but they really don't have to, nor should they.

You don't understand the use case. Suppose you want to make a set of objects with virtual functions that correspond to events. Also suppose you want this binding of events to virtual functions to be permanent, so you can reuse the same components as-is.

The only way to do the permanent binding is to use a message map, i.e. a static map of event bindings to virtual functions, like MFC does.

Programming message maps is cumbersome and has a lot of problems.

With your system, though, you can't even do message maps, since events do not have static ids.

Quote:

This really doesn't make sense though, because you can just call your specific function from the if (wmsg == ...) statement.

Remember, you want a permanent reusable binding.

Quote:

I don't understand this. Why would you continue an algorithm from an event detection anyway? And how would you integrate a callback for a widget into an algorithm instead? Shouldn't it be the other way around? That after detecting an event or inside a callback that you then call the algorithm?

Suppose a widget defines a procedure (not a piece of code, but a series of steps), and also suppose this procedure has steps that must be supplemented by the end-user programmer. With events, in order to have these procedure execute from start to finish, the programmer is forced to respond to the events. With callbacks, no such thing is required: the injected code (i.e. the steps supplemented by the end-user programmer) can be bound permanently to components.

EDIT:

For the first version of algui, I am planning to do the following widgets:

Static widgets:

  • label; text label.

  • image; image label.

  • frame; a rectangular area with a background and a border. The background will be a color, a picture, or a gradient (horizontal/vertical), and the border would be a bitmap, or a 3d sunken/raised rectangle.

Buttons:

  • push button; standard button with a click event.

  • check box; standard check box with a tick.

  • radio button; standard radio button with an associated group; only one button can be selected.

Bars:

  • scroll bar; either horizontal or vertical.

  • progress bar; either horizontal or vertical.

  • slider; either horizontal or vertical.

Editors:

  • text box; a single line text editor.

  • combo box; a combination of text box and a list view.

  • spin box; a text box with buttons that allow the change of the value with a device other than the keyboard.

Views:

  • list view; items arranged vertically in a list.

  • tree view; items arranged in a hierarchical view.

  • table view; items arranged in a grid.

Layout managers:

  • horizontal box; widgets laid out horizontally.

  • vertical box; widgets laid out vertically.

  • grid box; widgets laid out in a grid.

  • splitter; child widgets will be resizable via handle bars.

Standard dialogs:

  • message dialog; a dialog with a text message, an icon, and one or more buttons.

  • file dialog; save or load a file or choose a folder.

Menus:

  • menu; horizontal/vertical menu.

  • menu item; item of a menu.

What do you think about these? do you think there is something important left out?

Matthew Leverton

That basically looks like my list. I also have a tab strip and a tab panel. (The panel is a composite widget that contains a tab strip and one user widget associated with each tab.)

What about multiline text?

I assume a spacer/stretch "widget" comes with the layouts.

GullRaDriel

Where is the testing zip ? >:(

axilmar

Ah yes, tabs. I forgot about those.

I think it's best if I skip the multiline text editor and do a "graphical symbol" editor, one that allows 'text' lines to include graphics and shapes. In this way, one could have some rudimentary effects like bold or underlined text, lines made out of graphical symbols, small images like smiley faces etc.

And a spacer widget is a good idea.

Matthew Leverton

How are you going to handle borders around common widgets?

For instance, a button and a listbox may both have a border, but with a completely different style. The button may be rounded while the listbox is square.

Would you draw a border as part of the widget itself (and therefore included as part of the widget's dimensions), or would one have to wrap each item with an explicit frame widget?

Obviously it applies more to your native skin (and your personal preference), as somebody could draw a frame directly on a widget even if you originally did not.

axilmar

For instance, a button and a listbox may both have a border, but with a completely different style. The button may be rounded while the listbox is square.

A common base class with border and background properties.

Quote:

Would you draw a border as part of the widget itself (and therefore included as part of the widget's dimensions)

Yes.

Quote:

would one have to wrap each item with an explicit frame widget?

No.

It's simpler to have the widget draw the border itself. It requires fewer resources.

Thread #606344. Printed from Allegro.cc